Overview
Enterprise Knowledge gives AI agents access to your organization's source material during conversations — FAQs, warranty policies, support scripts, product catalogs. It closes the gap between general model knowledge and how your business actually operates.
Your content (web/PDF/text) → Knowledge Base → Indexed chunks
Agent query → Search → Ranked chunks → Inject into LLM prompt
Enterprise Knowledge is shared across your organization — it captures institutional content. It is distinct from Conversation Memory (twilio-conversation-memory), which is per-customer context. The two are designed to be combined: enterprise content for accuracy, customer memory for personalization.
Base URL: https://knowledge.twilio.com
Authentication: HTTP Basic — Authorization: Basic {base64(accountSid:authToken)}
Rules for agents:
- Always poll
statusUrlafter any 202 response — all writes are async - Always wait for Knowledge Base status
COMPLETEDbefore adding sources - Always wait for source processing to complete before searching
- Never use
/v1/paths — all routes use/v2/prefix - Never include auth headers when uploading to presigned URLs — they're already signed
- Never use spaces or underscores in
displayName— pattern is^[a-zA-Z0-9-]+$ - Never exceed 16MB per file upload or 1,048,576 chars per text source
Prerequisites
- Twilio account with Enterprise Knowledge enabled
— A credit card must be added to the account
— See
twilio-account-setupfor initial setup — Seetwilio-iam-auth-setupfor credential best practices - Environment variables:
TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN
- SDK:
pip install twilio/npm install twilio
Quickstart
Step 1 — Create a Knowledge Base
Python
import os, requests, time
account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
base_url = "https://knowledge.twilio.com"
auth = (account_sid, auth_token)
res = requests.post(
f"{base_url}/v2/ControlPlane/KnowledgeBases",
auth=auth,
json={"displayName": "product-docs", "description": "Support agent knowledge"}
)
status_url = res.json()["statusUrl"]
while True:
op = requests.get(status_url, auth=auth).json()
if op["status"] == "COMPLETED":
kb_id = op["result"]["id"]
break
if op["status"] == "FAILED":
raise Exception(op["error"]["detail"])
time.sleep(2)
print(kb_id) # know_knowledgebase_xxx
Node.js
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const baseUrl = "https://knowledge.twilio.com";
const authHeader = "Basic " + btoa(`${accountSid}:${authToken}`);
const headers = { "Authorization": authHeader, "Content-Type": "application/json" };
const res = await fetch(`${baseUrl}/v2/ControlPlane/KnowledgeBases`, {
method: "POST",
headers,
body: JSON.stringify({ displayName: "product-docs", description: "Support agent knowledge" }),
});
const { statusUrl } = await res.json();
let kbId;
while (true) {
const op = await fetch(statusUrl, { headers: { "Authorization": authHeader } }).then(r => r.json());
if (op.status === "COMPLETED") { kbId = op.result.id; break; }
if (op.status === "FAILED") throw new Error(op.error.detail);
await new Promise(r => setTimeout(r, 2000));
}
Step 2 — Add a Knowledge Source
Three source types: Web (crawl a URL), File (upload PDF/CSV/Markdown/text, max 16MB), Text (inline, max 1,048,576 chars).
Python
knowledge = requests.post(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge",
auth=auth,
json={
"name": "Product Documentation",
"description": "Public product docs",
"source": {"type": "Web", "url": "https://docs.example.com", "crawlDepth": 3}
}
).json()
knowledge_id = knowledge["id"]
Node.js
const knowledge = await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge`, {
method: "POST",
headers,
body: JSON.stringify({
name: "Product Documentation",
description: "Public product docs",
source: { type: "Web", url: "https://docs.example.com", crawlDepth: 3 },
}),
}).then(r => r.json());
const knowledgeId = knowledge.id;
Step 3 — Wait for processing
Sources are processed asynchronously. Poll until status is COMPLETED.
Python
while True:
k = requests.get(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}", auth=auth
).json()
if k["status"] == "COMPLETED":
break
if k["status"] == "FAILED":
raise Exception(f"Processing failed: {k}")
time.sleep(3)
Node.js
while (true) {
const k = await fetch(
`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge/${knowledgeId}`,
{ headers: { "Authorization": authHeader } }
).then(r => r.json());
if (k.status === "COMPLETED") break;
if (k.status === "FAILED") throw new Error(JSON.stringify(k));
await new Promise(r => setTimeout(r, 3000));
}
Statuses: SCHEDULED → QUEUED → PROCESSING → COMPLETED / FAILED
Step 4 — Search and inject into LLM prompt
Python
results = requests.post(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Search",
auth=auth,
json={"query": "How do I reset my password?", "top": 5}
).json()
chunks = "\n\n".join(c["content"] for c in results["chunks"])
system_prompt = f"""You are a helpful support agent.
Relevant knowledge:
{chunks}
Answer using only the above content."""
Node.js
const results = await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Search`, {
method: "POST",
headers,
body: JSON.stringify({ query: "How do I reset my password?", top: 5 }),
}).then(r => r.json());
const chunks = results.chunks.map(c => c.content).join("\n\n");
const systemPrompt = `You are a helpful support agent.\n\nRelevant knowledge:\n${chunks}`;
Key Patterns
Combine with Conversation Memory
For the best agent responses, combine Enterprise Knowledge (company content) with Conversation Memory Recall (individual customer history).
Python
recall_res = requests.post(
f"https://memory.twilio.com/v1/Stores/{store_id}/Profiles/{profile_id}/Recall",
auth=auth,
json={"query": user_query, "observationsLimit": 5}
).json()
search_res = requests.post(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Search",
auth=auth,
json={"query": user_query, "top": 3}
).json()
customer_context = "\n".join(o["content"] for o in recall_res.get("observations", []))
knowledge = "\n\n".join(c["content"] for c in search_res.get("chunks", []))
system_prompt = f"""Customer history:\n{customer_context}\n\nDocumentation:\n{knowledge}"""
Node.js
const [recallRes, searchRes] = await Promise.all([
fetch(`https://memory.twilio.com/v1/Stores/${storeId}/Profiles/${profileId}/Recall`, {
method: "POST",
headers,
body: JSON.stringify({ query: userQuery, observationsLimit: 5 }),
}).then(r => r.json()),
fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Search`, {
method: "POST",
headers,
body: JSON.stringify({ query: userQuery, top: 3 }),
}).then(r => r.json()),
]);
const customerContext = recallRes.observations.map(o => o.content).join("\n");
const knowledge = searchRes.chunks.map(c => c.content).join("\n\n");
File Upload (PDF/CSV/Markdown)
File sources return a presigned URL. Upload the file there — do not include auth headers.
Python
knowledge = requests.post(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge",
auth=auth,
json={"name": "Handbook", "description": "Employee handbook", "source": {"type": "File"}}
).json()
upload_url = knowledge["source"]["importUrl"]
with open("handbook.pdf", "rb") as f:
requests.put(upload_url, data=f, headers={"Content-Type": "application/pdf"})
Node.js
const knowledge = await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge`, {
method: "POST",
headers,
body: JSON.stringify({ name: "Handbook", description: "Employee handbook", source: { type: "File" } }),
}).then(r => r.json());
const file = await fs.promises.readFile("handbook.pdf");
await fetch(knowledge.source.importUrl, {
method: "PUT",
headers: { "Content-Type": "application/pdf" },
body: file,
});
Filter Search to Specific Sources
When your Knowledge Base has multiple sources, target search to specific ones:
Python
results = requests.post(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Search",
auth=auth,
json={"query": "cancellation policy", "top": 5, "knowledgeIds": [policy_source_id]}
).json()
for chunk in results["chunks"]:
print(f"[{chunk['score']:.3f}] {chunk['content'][:100]}")
Node.js
const results = await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Search`, {
method: "POST",
headers,
body: JSON.stringify({ query: "cancellation policy", top: 5, knowledgeIds: [policySourceId] }),
}).then(r => r.json());
for (const chunk of results.chunks) {
console.log(`[${chunk.score.toFixed(3)}] ${chunk.content.slice(0, 100)}`);
}
Omit knowledgeIds to search across all sources. Max 100 IDs per request.
Refresh a Web Source
Re-crawl without changing config. Set crawlPeriod for automatic recrawling.
Python
requests.patch(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}?refresh=true",
auth=auth,
json={"name": "Product Documentation"}
)
requests.patch(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}",
auth=auth,
json={"name": "Product Documentation", "source": {"type": "Web", "crawlPeriod": "WEEKLY"}}
)
Node.js
await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge/${knowledgeId}?refresh=true`, {
method: "PATCH",
headers,
body: JSON.stringify({ name: "Product Documentation" }),
});
await fetch(`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge/${knowledgeId}`, {
method: "PATCH",
headers,
body: JSON.stringify({ name: "Product Documentation", source: { type: "Web", crawlPeriod: "WEEKLY" } }),
});
Crawl period options: WEEKLY | BIWEEKLY | MONTHLY | NEVER
Inspect Chunks
Audit what was indexed from a source:
Python
chunks = requests.get(
f"{base_url}/v2/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}/Chunks",
auth=auth,
params={"pageSize": 50}
).json()
for chunk in chunks["chunks"]:
print(f"[{chunk['metadata']['sourceType']}] {chunk['content'][:100]}")
Node.js
const chunks = await fetch(
`${baseUrl}/v2/KnowledgeBases/${kbId}/Knowledge/${knowledgeId}/Chunks?pageSize=50`,
{ headers: { "Authorization": authHeader } }
).then(r => r.json());
for (const chunk of chunks.chunks) {
console.log(`[${chunk.metadata.sourceType}] ${chunk.content.slice(0, 100)}`);
}
Paginate with pageToken from chunks.meta.nextToken.
CANNOT
- Cannot exceed 5 Knowledge Bases per account
- Cannot exceed 10 knowledge sources per Knowledge Base
- Cannot add sources before Knowledge Base is active — poll
statusUrluntilCOMPLETED - Cannot use v1 endpoints — all routes use
/v2/prefix onknowledge.twilio.com - Cannot include auth header when uploading to presigned URL —
importUrlis already signed - Cannot search before source processing completes — poll source status first
- Cannot exceed 16 MiB (16,777,216 bytes) per file upload
- Cannot exceed 1,048,576 characters (~1MB) per text source pushed via API
- Cannot exceed 2048 characters in a search query
- Cannot exceed 2048 characters in a web source URL
- Cannot retrieve more than 20 search results per query (
topmax is 20) - Cannot exceed 100
knowledgeIdsin a search filter - Cannot set crawl depth beyond 1–10 levels for web sources
- Cannot use custom crawl schedules — locked to fixed intervals:
WEEKLY,BIWEEKLY,MONTHLY, orNEVER - Cannot use spaces or underscores in
displayName— alphanumeric and hyphens only - Cannot use
namelonger than 30 characters for knowledge sources - Cannot modify immutable fields (
id,type,status,url,createdAt,updatedAt) via PATCH - Cannot use Knowledge for per-customer context — use
twilio-conversation-memoryfor that
Next Steps
- Per-customer context:
twilio-conversation-memory— combine with Enterprise Knowledge for full agent context - Background transcript intelligence:
twilio-conversation-intelligence - Voice agent with ConversationRelay:
twilio-voice-conversation-relay - TAC SDK integration:
twilio-agent-connect - Debug integration issues:
twilio-debugging-observability