> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.ada.cx/reference/knowledge/articles/bulk-upsert/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ada.cx/_mcp/server. # Upsert multiple articles POST https://example.ada.support/api/v2/knowledge/bulk/articles/ Content-Type: application/json Upsert an array of knowledge articles This endpoint will create or update articles based on the unique `id` field of each article. If an article with the same `id` already exists, it will be updated. Otherwise, a new article will be created. **Limits:** * The maximum size of a request payload is 10MB * The maximum size of an article is 100KB * The maximum number of articles is 50,000 by default. Higher limits are available for eligible plans — contact your Ada team. **Behavior at the article limit:** Requests that only update existing articles (every `id` in the request already exists) continue to succeed even when your knowledge base is at its article limit. A request that introduces any new article `id` while at the limit is rejected as a whole with a `400` response. The error message is `Maximum article limit of {N} exceeded`, where `{N}` is your article limit, and the error details include a `code` of `knowledge.articles.total_exceeded`, the list of `new_article_ids` that triggered the rejection (capped at the first 100), and the total `new_article_count`. Use these fields to separate new articles from update-only batches, which can still be submitted. Reference: https://docs.ada.cx/reference/knowledge/articles/bulk-upsert ## Authentication - `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer `, where token is your auth token. ## Request ### Body (application/json) This endpoint expects a list of KnowledgeArticleUpsertRequest. - `list of KnowledgeArticleUpsertRequest` ## Response ### 200 Articles upserted - `list of KnowledgeArticleUpsertResponse` ## Errors ### 400 Bad Request Error Bad Request - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 401 Unauthorized Error Unauthorized - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 429 Too Many Requests Error Too Many Requests - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 500 Internal Server Error Internal Server Error - `errors` (list of ErrorsErrorsItems, required) — A list of errors ## Types ### KnowledgeArticleUpsertRequest - `id` (string, required) — A unique identifier for the article - `name` (string, required) — The name or title of the article - `content` (string, required) — The content of the article in markdown format - `knowledge_source_id` (string, required) — The id of the `knowledge_source` the article belongs to - `url` (string, optional, nullable) — The url of the article - `tag_ids` (list of string, optional) — A list of ids for the tags associated with the article - `language` (string, optional) — The IETF BCP 47 language code for the article, defaults to `en` - `external_created` (string, optional, nullable) — The date the article was created in the source system - `external_updated` (string, optional, nullable) — The date the article was last updated in the source system - `enabled` (boolean, optional) — Whether the article should be referenced during response generation, defaults to `true` - `metadata` (KnowledgeArticleUpsertRequestMetadata, optional, nullable) — A dictionary of arbitrary key,value pairs. This data is not used by Ada, but can be used by the client to store additional information about the article. - `availability_rules` (AvailabilityRule, optional, nullable) — Availability rule controlling which articles the AI Agent can access during a conversation. Send a rule object to attach or replace a rule, `null` to detach an existing rule, or omit the field to leave any existing rule unchanged. A rule may contain at most 1000 conditions in total. See [Availability rules](/reference/introduction/availability-rules) for the full schema and examples. ### KnowledgeArticleUpsertResponse - `id` (string, required) — A unique identifier for the article - `success` (boolean, optional) — Whether the article was successfully created/updated - `created` (boolean, optional) — `True` if a new article was created, `false` if an existing article was updated ### ErrorsErrorsItems - `type` (string, required) — The error type - `message` (string, required) — The error message - `details` (string, optional, nullable) — Extra information about the error ### KnowledgeArticleUpsertRequestMetadata A dictionary of arbitrary key,value pairs. This data is not used by Ada, but can be used by the client to store additional information about the article. ### AvailabilityRule A two-level tree of conditions that determines availability based on variable values during a conversation. The root group has a `match` combinator and a list of conditions or condition groups. A rule may contain at most 1000 conditions in total, counting every condition across the root and all nested condition groups. - `match` (enum, required) — Whether all conditions must pass (`all`) or any one condition must pass (`any`). - Allowed values: `all`, `any` - `conditions` (list of AvailabilityRuleConditionsItems, required) — List of conditions or condition groups. Must contain at least one entry. Each entry is either a `Condition` object (with `variable`, `operator`, and optional `value`) or a `ConditionGroup` object (with its own `match` and nested `conditions` list). ### AvailabilityRuleConditionsItems ### AvailabilityRuleCondition A single condition comparing a variable to a value. - `variable` (AvailabilityRuleConditionVariable, required) — The variable to test, referenced by `id`. Look up the ids for your Agent through the variables endpoint. Not every variable can be used in a rule; referencing one that can't returns a `400`. - `operator` (enum, required) — The comparison operator. Unary operators (`is_set`, `is_not_set`) must not include a `value` field. - Allowed values: `equals`, `does_not_equal`, `greater_than`, `less_than`, `starts_with`, `ends_with`, `contains`, `does_not_contain`, `is_set`, `is_not_set` - `value` (AvailabilityRuleConditionValue, optional) — The value to compare against. Omit for unary operators (`is_set`, `is_not_set`). - `case_sensitive` (boolean, optional) — Whether the comparison is case-sensitive. Defaults to `false`. Only meaningful for the equality (`equals`, `does_not_equal`) and string (`starts_with`, `ends_with`, `contains`, `does_not_contain`) operators. Omitted from responses when `false`. ### AvailabilityRuleConditionGroup A nested group of conditions inside a rule's top-level `conditions` list. Condition groups may contain only `Condition` objects — further nesting is not supported. - `match` (enum, required) — Whether all conditions must pass (`all`) or any one condition must pass (`any`). - Allowed values: `all`, `any` - `conditions` (list of AvailabilityRuleCondition, required) — List of conditions inside this condition group. Must contain at least one entry. ### AvailabilityRuleConditionVariable The variable to test, referenced by `id`. Look up the ids for your Agent through the variables endpoint. Not every variable can be used in a rule; referencing one that can't returns a `400`. - `id` (string, required) — The id of the variable. ### AvailabilityRuleConditionValue The value to compare against. Omit for unary operators (`is_set`, `is_not_set`). ## Examples **Request** ```json [ { "id": "5df263b7db5a7e6ea03fae9b", "name": "How to reset your password", "content": "# How to reset your password\\n\\n1. Go to the login page\\n2. Click on the \"Forgot password\" link\\n3. Follow the instructions", "knowledge_source_id": "5df263b7db5a7e6ea03fae9b", "availability_rules": { "match": "all", "conditions": [ { "variable": { "id": "5df263b7db5a7e6ea03fae9b" }, "operator": "equals", "value": "en" } ] } } ] ``` **SDK Code** ```python import requests url = "https://example.ada.support/api/v2/knowledge/bulk/articles/" payload = [ { "id": "5df263b7db5a7e6ea03fae9b", "name": "How to reset your password", "content": "# How to reset your password\n\n1. Go to the login page\n2. Click on the \"Forgot password\" link\n3. Follow the instructions", "knowledge_source_id": "5df263b7db5a7e6ea03fae9b", "availability_rules": { "match": "all", "conditions": [ { "variable": { "id": "5df263b7db5a7e6ea03fae9b" }, "operator": "equals", "value": "en" } ] } } ] headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://example.ada.support/api/v2/knowledge/bulk/articles/'; const options = { method: 'POST', headers: {Authorization: 'Bearer ', 'Content-Type': 'application/json'}, body: '[{"id":"5df263b7db5a7e6ea03fae9b","name":"How to reset your password","content":"# How to reset your password\\n\\n1. Go to the login page\\n2. Click on the \"Forgot password\" link\\n3. Follow the instructions","knowledge_source_id":"5df263b7db5a7e6ea03fae9b","availability_rules":{"match":"all","conditions":[{"variable":{"id":"5df263b7db5a7e6ea03fae9b"},"operator":"equals","value":"en"}]}}]' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://example.ada.support/api/v2/knowledge/bulk/articles/" payload := strings.NewReader("[\n {\n \"id\": \"5df263b7db5a7e6ea03fae9b\",\n \"name\": \"How to reset your password\",\n \"content\": \"# How to reset your password\\\\n\\\\n1. Go to the login page\\\\n2. Click on the \\\"Forgot password\\\" link\\\\n3. Follow the instructions\",\n \"knowledge_source_id\": \"5df263b7db5a7e6ea03fae9b\",\n \"availability_rules\": {\n \"match\": \"all\",\n \"conditions\": [\n {\n \"variable\": {\n \"id\": \"5df263b7db5a7e6ea03fae9b\"\n },\n \"operator\": \"equals\",\n \"value\": \"en\"\n }\n ]\n }\n }\n]") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "Bearer ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://example.ada.support/api/v2/knowledge/bulk/articles/") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = 'Bearer ' request["Content-Type"] = 'application/json' request.body = "[\n {\n \"id\": \"5df263b7db5a7e6ea03fae9b\",\n \"name\": \"How to reset your password\",\n \"content\": \"# How to reset your password\\\\n\\\\n1. Go to the login page\\\\n2. Click on the \\\"Forgot password\\\" link\\\\n3. Follow the instructions\",\n \"knowledge_source_id\": \"5df263b7db5a7e6ea03fae9b\",\n \"availability_rules\": {\n \"match\": \"all\",\n \"conditions\": [\n {\n \"variable\": {\n \"id\": \"5df263b7db5a7e6ea03fae9b\"\n },\n \"operator\": \"equals\",\n \"value\": \"en\"\n }\n ]\n }\n }\n]" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://example.ada.support/api/v2/knowledge/bulk/articles/") .header("Authorization", "Bearer ") .header("Content-Type", "application/json") .body("[\n {\n \"id\": \"5df263b7db5a7e6ea03fae9b\",\n \"name\": \"How to reset your password\",\n \"content\": \"# How to reset your password\\\\n\\\\n1. Go to the login page\\\\n2. Click on the \\\"Forgot password\\\" link\\\\n3. Follow the instructions\",\n \"knowledge_source_id\": \"5df263b7db5a7e6ea03fae9b\",\n \"availability_rules\": {\n \"match\": \"all\",\n \"conditions\": [\n {\n \"variable\": {\n \"id\": \"5df263b7db5a7e6ea03fae9b\"\n },\n \"operator\": \"equals\",\n \"value\": \"en\"\n }\n ]\n }\n }\n]") .asString(); ``` ```php request('POST', 'https://example.ada.support/api/v2/knowledge/bulk/articles/', [ 'body' => '[ { "id": "5df263b7db5a7e6ea03fae9b", "name": "How to reset your password", "content": "# How to reset your password\\\\n\\\\n1. Go to the login page\\\\n2. Click on the \\"Forgot password\\" link\\\\n3. Follow the instructions", "knowledge_source_id": "5df263b7db5a7e6ea03fae9b", "availability_rules": { "match": "all", "conditions": [ { "variable": { "id": "5df263b7db5a7e6ea03fae9b" }, "operator": "equals", "value": "en" } ] } } ]', 'headers' => [ 'Authorization' => 'Bearer ', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://example.ada.support/api/v2/knowledge/bulk/articles/"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "[\n {\n \"id\": \"5df263b7db5a7e6ea03fae9b\",\n \"name\": \"How to reset your password\",\n \"content\": \"# How to reset your password\\\\n\\\\n1. Go to the login page\\\\n2. Click on the \\\"Forgot password\\\" link\\\\n3. Follow the instructions\",\n \"knowledge_source_id\": \"5df263b7db5a7e6ea03fae9b\",\n \"availability_rules\": {\n \"match\": \"all\",\n \"conditions\": [\n {\n \"variable\": {\n \"id\": \"5df263b7db5a7e6ea03fae9b\"\n },\n \"operator\": \"equals\",\n \"value\": \"en\"\n }\n ]\n }\n }\n]", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "Bearer ", "Content-Type": "application/json" ] let parameters = [ [ "id": "5df263b7db5a7e6ea03fae9b", "name": "How to reset your password", "content": "# How to reset your password\n\n1. Go to the login page\n2. Click on the \"Forgot password\" link\n3. Follow the instructions", "knowledge_source_id": "5df263b7db5a7e6ea03fae9b", "availability_rules": [ "match": "all", "conditions": [ [ "variable": ["id": "5df263b7db5a7e6ea03fae9b"], "operator": "equals", "value": "en" ] ] ] ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://example.ada.support/api/v2/knowledge/bulk/articles/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```