PageSourceSearch

https://docsbot.ai/_next/static/chunks/pages/documentation/developer/authentication-64c95d4e4221f322.js

js docsbot.ai collected 2026-09-24 08:41:39 UTC 11,866 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[45012],{90012:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/documentation/developer/authentication",function(){return t(18659)}])},18659:function(e,n,t){"use strict";t.r(n),t.d(n,{__N_SSG:function(){return m},default:function(){return b},markdoc:function(){return y}});var o=t(54128),a=t(45887),s=t(34575),r=t(9926),i=t(65963),d=t(69725);let u={tags:(0,r.w)(i),nodes:(0,r.w)(d),functions:(0,r.w)({}),...(0,r.w)({})},c=new s.ZP.Tokenizer({allowComments:!0}).tokenize("---\ntitle: Authentication\ndescription: Chat APIs on api.docsbot.ai (API key, JWT, HMAC) and Admin APIs on docsbot.ai/api (API key only).\n---\n\nAuthentication depends on which host you call: **Chat APIs** (`https://api.docsbot.ai`) vs **Admin APIs** (`https://docsbot.ai/api/`). {% .lead %}\n\n---\n\n## Teams and Permissions\n\n### What is a team?\n\nA team is the basic root of a DocsBot account. Plans and limits are tied to a team, which has a collection of bots and their sources. Multiple user accounts can be assigned to a team. Each team has a unique ID and a name.\n\n### Key permissions\n\nAPI keys are unique to each user account and their permissions mirror that of your user account. For example, if your user account has access to multiple teams, your API key will also have access to all of those teams.\n\n## Getting your API key\n\nTo use our APIs, you need to get an API key. You can get your API key from the [API Keys](https://docsbot.ai/app/api) section of your dashboard. This is the key associated with your user account and will be the same no matter which team dashboard you are in. You can create or change your API key at any time by clicking the \"Change\" button. When you change your key, all previous API requests will stop working until you configure them to use the new key.\n\n{% callout type=\"warning\" title=\"Don't forget to copy!\" %}\nAPI keys are only shown once as we store them safely hashed, so make sure you copy it to a safe place. If you lose or forget your key you will have to create a new one.\n{% /callout %}\n\n## Chat APIs (`https://api.docsbot.ai`)\n\nChat Agent, conversations, questions, and other **bot** endpoints on this host—including traffic from the **embeddable chat widget**—use:\n\n```http\nAuthorization: Bearer <token>\n```\n\n`<token>` may be:\n\n- Your **user API key** from [Getting your API key](#getting-your-api-key) (server-side / trusted environments).\n- For **private** (or optionally **public**) bots, a **signed JWT** or **legacy HMAC** string instead of (or alongside) flows that require embed signing—see [Private bots](#private-bots).\n\n### Private bots\n\nBots with **private** visibility require a valid Bearer token. **We recommend** a **signed JWT**; **legacy HMAC** and **user API key** Bearer auth are also supported.\n\n1. **Signed JWT (recommended)** — Sign with **HS256** using your bot’s **signature key** from the [Bots](https://docsbot.ai/app/bots) widget embed page. The JWT payload **must** include **`team_id` and `bot_id`** for that bot, plus **`exp` and `iat`** (see below). You may include **`metadata`** for trusted keys such as `priv_*` (e.g. Stripe customer id); see [Chat Agent API — Trusted private metadata with JWT](/documentation/developer/chat-agent#trusted-private-metadata-with-jwt) and [Stripe Actions](/documentation/developer/stripe-actions).\n\n2. **HMAC token** — Compute HMAC-SHA256 of `botId:expires` with the same signature key, format as **`hex:expires`**, and send that string as the Bearer token. While still supported; JWT is preferred for signing metadata.\n\n3. **User API key** — The same dashboard key from [Getting your API key](#getting-your-api-key). Supported for private bots when the call runs from a **trusted server**. **Never** expose API keys in public browser code; for embedded widgets on the public web, use a JWT or HMAC `signature` instead.\n\n**Public bots** can be called without a token; sending a valid token can still unlock higher limits and authenticated-only options on some endpoints.\n\n#### JWT claims: `exp` and `iat`\n\nThese are standard **registered JWT claims**. Values are **Unix timestamps in seconds** (count from 1970-01-01 UTC), not milliseconds.\n\n- **`iat`** (_issued at_) — When the token was minted; typically set to the current time. Lets validators reason about freshness and clock skew.\n- **`exp`** (_expiration time_) — The last second the token is considered valid; after this the API rejects it. Use a **short TTL** (for example one hour) so a leaked token stops working quickly. We will show a user friendly message to refresh the page if a widget tries to use an expired token.\n\n#### Example: signed JWT (HS256)\n\nUse your bot’s **signature key** (widget embed page) as the HMAC secret. Install a JWT library for your language: **`jose`** or **`jsonwebtoken`** (Node), **`firebase/php-jwt`** (PHP), **`PyJWT`** (Python).\n\nPayload must include `team_id`, `bot_id`, `iat`, `exp`, and optionally `metadata`.\n\n**Node.js** (`jose`):\n\n```js\nimport * as jose from 'jose'\n\nconst signatureKey = process.env.DOCSBOT_SIGNATURE_KEY // from Widget embed page\nconst teamId = 'YOUR_TEAM_ID'\nconst botId = 'YOUR_BOT_ID'\n\nconst now = Math.floor(Date.now() / 1000)\nconst exp = now + 60 * 60 // 1 hour\nconst secret = new TextEncoder().encode(signatureKey)\n\nconst jwt = await new jose.SignJWT({\n  team_id: teamId,\n  bot_id: botId,\n  metadata: {}, // optional: e.g. priv_* for Stripe — see Stripe Actions doc\n})\n  .setProtectedHeader({ alg: 'HS256' })\n  .setIssuedAt(now)\n  .setExpirationTime(exp)\n  .sign(secret)\n\n// Authorization: Bearer <jwt>\n```\n\n**Node.js** (`jsonwebtoken`):\n\n```js\nconst jwt = require('jsonwebtoken')\n\nconst signatureKey = process.env.DOCSBOT_SIGNATURE_KEY\nconst now = Math.floor(Date.now() / 1000)\nconst payload = {\n  iat: now,\n  exp: now + 60 * 60,\n  team_id: 'YOUR_TEAM_ID',\n  bot_id: 'YOUR_BOT_ID',\n  metadata: {},\n}\n\nconst token = jwt.sign(payload, signatureKey, { algorithm: 'HS256' })\n// Authorization: Bearer <token>\n```\n\n**PHP** (`firebase/php-jwt`):\n\n```php\n<?php\n\nuse Firebase\\JWT\\JWT;\n\n$signatureKey = 'SIGNATURE_KEY_FROM_BOT_WIDGET_EMBED_PAGE';
1\n$now = time();\n\n$payload = [\n    'iat' => $now,\n    'exp' => $now + 3600,\n    'team_id' => 'YOUR_TEAM_ID',\n    'bot_id' => 'YOUR_BOT_ID',\n    'metadata' => new stdClass(), // or [ 'priv_stripe_customer_id' => 'cus_...' ]\n];\n\n$jwt = JWT::encode($payload, $signatureKey, 'HS256');\n// Authorization: Bearer <jwt>\n```\n\n**Python** (`PyJWT`):\n\n```python\nimport time\nimport jwt\n\nsignature_key = 'SIGNATURE_KEY_FROM_BOT_WIDGET_EMBED_PAGE'\nnow = int(time.time())\npayload = {\n    'iat': now,\n    'exp': now + 3600,\n    'team_id': 'YOUR_TEAM_ID',\n    'bot_id': 'YOUR_BOT_ID',\n    'metadata': {},\n}\ntoken = jwt.encode(payload, signature_key, algorithm='HS256')\n```\n\n#### Example: legacy HMAC token\n\nSame **signature key** as the JWT. Message to MAC is the string `botId:expires` (same `expires` Unix seconds you append after the colon in the token).\n\n**Node.js:**\n\n```js\nimport crypto from 'crypto'\n\nconst botId = 'YOUR_BOT_ID'\nconst embedKey = 'SIGNATURE_KEY_FROM_BOT_WIDGET_EMBED_PAGE'\n\nconst hmac = crypto.createHmac('sha256', embedKey)\nconst expires = Math.floor(Date.now() / 1000) + 60 * 60 * 1 // expires in 1 hour\nhmac.update(`${botId}:${expires}`)\nconst signature = `${hmac.digest('hex')}:${expires}`\n// HTTP: Authorization: Bearer <signature>\n// Widget: pass `signature` into DocsBotAI.init (see embeddable widget docs)\n```\n\n**PHP:**\n\n```php\n$botId = 'YOUR_BOT_ID';\n$embedKey = 'SIGNATURE_KEY_FROM_BOT_WIDGET_EMBED_PAGE';\n\n$expires = time() + 60 * 60 * 1;\n$signature = hash_hmac('sha256', $botId . ':' . $expires, $embedKey) . ':' . $expires;\n```\n\n**Python:**\n\n```python\nimport hashlib\nimport hmac\nimport time\n\nbot_id = 'YOUR_BOT_ID'\nembed_key = 'SIGNATURE_KEY_FROM_BOT_WIDGET_EMBED_PAGE'\n\nexpires = int(time.time()) + 60 * 60 * 1\nsignature = hmac.new(embed_key.encode(), f'{bot_id}:{expires}'.encode(), hashlib.sha256).hexdigest() + f':{expires}'\n```\n\n> **Note:** In HMAC snippets, `bot_id` is the bot’s ID (the second UUID in the widget `id` string, e.g. for `teamPart/botPart` use `botPart`).\n\n**Embeddable widget (JWT/HMAC)** — after generating the JWT/HMAC server-side, pass the JWT or `hex:expires` string as `signature` in the embed code.\n\n```js\nDocsBotAI.init({\n  id: 'YOUR_ID_HERE',\n  signature: 'f95b5d6431fe76854fe14384123225cff9501ed6cd08dff12c1897a93badabbc:1693821134',\n})\n```\n\nFull widget context: [Embedding private bots](/documentation/developer/embeddable-chat-widget#embedding-private-bots).\n\n### Websocket API endpoints (Legacy Streaming)\n\nFor the streaming APIs, websockets do not support Authorization headers. You should send the API key as an `auth` parameter with the questions. For example:\n\n```javascript\n// Send message to server when connection is established\nws.onopen = function (event) {\n  const req = { question: 'What is WordPress?', full_source: false, history: [], auth: '1234567890' }\n  ws.send(JSON.stringify(req))\n}\n```\n\n{% callout type=\"warning\" title=\"Do not expose your API key!\" %}\nAPI keys are meant to be used server-side, and should never be exposed to the public in JavaScript. If you are using a client-side library, make sure you are not exposing your API key to the public by proxying requests through your own server.\n{% /callout %}\n\n## Admin APIs (`https://docsbot.ai/api/`)\n\nDashboard **admin** routes—teams, bots, sources, members, leads, and everything under `/api/` on **docsbot.ai**—accept **only** your [user API key](#getting-your-api-key) in the header. **JWT and HMAC are not supported.**\n\n```http\nAuthorization: Bearer <your-api-key>\n```\n\n### Examples\n\nGET `/api/teams/` with your API key:\n\n#### cURL\n\n```bash\ncurl --request GET 'https://docsbot.ai/api/teams/' \\\n--header 'Authorization: Bearer 2e9fd6965890e80b9a7bb271900ab859e199a5778f851b73d97136d3495849ef'\n```\n\n#### JavaScript Fetch\n\n```javascript\nvar myHeaders = new Headers();\nmyHeaders.append(\"Authorization\", \"Bearer 2e9fd6965890e80b9a7bb271900ab859e199a5778f851b73d97136d3495849ef\");\n\nvar requestOptions = {\n  method: 'GET',\n  headers: myHeaders,\n  redirect: 'follow'\n};\n\nfetch(\"https://docsbot.ai/api/teams/\", requestOptions)\n  .then(response => response.text())\n  .then(result => console.log(result))\n  .catch(error => console.log('error', error));\n```\n\n#### PHP cURL\n\n```php\n<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, array(\n  CURLOPT_URL => 'https://docsbot.ai/api/teams/',\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_ENCODING => '',\n  CURLOPT_MAXREDIRS => 1,\n  CURLOPT_TIMEOUT => 0,\n  CURLOPT_FOLLOWLOCATION => true,\n  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n  CURLOPT_CUSTOMREQUEST => 'GET',\n  CURLOPT_HTTPHEADER => array(\n    'Authorization: Bearer 2e9fd6965890e80b9a7bb271900ab859e199a5778f851b73d97136d3495849ef'\n  ),\n));\n\n$response = curl_exec($curl);\n\ncurl_close($curl);\necho $response;\n```\n\n#### Python\n\n```python\nimport requests\n\nurl = \"https://docsbot.ai/api/teams/\"\n\npayload={}\nheaders = {\n  'Authorization': 'Bearer 2e9fd6965890e80b9a7bb271900ab859e199a5778f851b73d97136d3495849ef'\n}\n\nresponse = requests.request(\"GET\", url, headers=headers, data=payload)\n\nprint(response.text)\n```\n"),p=s.ZP.parse(c,{slots:!1}),h=p.attributes.frontmatter?a.ZP.load(p.attributes.frontmatter):{},{components:l}=(0,r.J)(u);var m=!0;let y={frontmatter:h};function b(e){let n=e.markdoc;return s.RZ.react(n.content,o,{components:{...l,...e.components}})}}},function(e){e.O(0,[22649,56254,40274,49774,92888,40179],function(){return e(e.s=90012)}),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.