POST /v1/graph/insert, /delete, and /update add, remove, and replace
individual documents in the existing categories of a built knowledge graph.
POST /v1/graph/recluster reorganizes the communities of a partition that has
changed a lot. For when to use them, see
Incremental Graph Updates. For a step-by-step guide, see the
Quick Start. For how the divergence is measured, see
Divergence and Reorganization.
The endpoints are relative to the path of the project’s AutoGraph service,
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}.
service_id_postfix is the end of the service ID, such as vyk1t for
arangodb-autograph-vyk1t. The serviceUrl in the project metadata of
GET /_platform/acp/v1/project_by_name/{database}/{project}
ends with it, for example .../autograph/vyk1t/.
| Endpoint | Operation | Purpose | Call |
|---|---|---|---|
POST /v1/graph/insert | Add | Add new documents to an existing category | Synchronous |
POST /v1/graph/delete | Remove | Remove documents that are in an existing category, with all their graph data and their files in the File Manager | Synchronous |
POST /v1/graph/update | Replace | Replace documents that are in an existing category with a new version | Asynchronous |
POST /v1/graph/recluster | Reorganize | Rebuild the communities of a partition with the FullGraphRAG strategy that has changed a lot | Asynchronous |
POST /v1/orchestrate with file_ids | Knowledge graph build | Add the added and replaced documents to the knowledge graph | Asynchronous |
The /v1/graph/* endpoints work on one existing category, which you set in
category. A category is the label you give a document when you upload it to
the File Manager, the second part of its scope [project, category]. Files
imported with the legacy module
field use that label instead. The endpoints cannot create a category. To add
one, build it, see When to use IGU.
Add documents
Adds new documents to an existing category. The API calls this operation insert. The call is synchronous.
The corpus and the target category have to exist already. If the project has
exactly one category, you can omit category. Otherwise, it is required.
Request
Every document is identified by its File Manager file_id. Adding takes no
inline content, so upload the document with
POST /_platform/filemanager/_db/{database}/rag-input
first and pass the returned ID.
{
"files": [
{
"doc_name": "new-contract.pdf",
"file_id": "<file-manager-file-id>"
}
],
"category": "legal"
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
files | object[] | Yes | The documents to add. The list cannot be empty, cannot contain duplicate doc_name or file_id values, and cannot have more than 100 files per request. |
files[].doc_name | string | Yes | The file name of the document. It has to match the File Manager file name of file_id. |
files[].file_id | string | Yes | The File Manager ID to get the document from. Every entry needs one. If any entry is missing it, the whole batch is rejected with 400 and a message that lists the offending doc_name values. |
category | string | Conditional | The existing category to add the documents to. Required unless the project has exactly one category. |
There is no citable_url request field. AutoGraph reads the citable URL from
the custom_metadata of the File Manager file and validates it the same way a
corpus build does, so it has to be
an http or https URL with valid characters. Set it on the file when you
upload it.
Response
{
"results": [
{
"doc_name": "new-contract.pdf",
"success": true,
"cluster_key": "cluster_legal_0",
"rag_partition_id": "legal_0_a"
}
]
}
| Field | Meaning |
|---|---|
doc_name | The document this result refers to. |
success | Whether the document has been added. |
error_message | Set if this file failed. The other files of the batch can still succeed. |
cluster_key | The existing cluster that has been selected for the document. Can be empty if there is no suitable neighbor in a cluster. |
rag_partition_id | The knowledge graph partition of the selected cluster. Can be empty if there is no strategy profile yet. |
file_id | The File Manager ID of the added document. Use this value in the file_ids of the knowledge graph build. |
The response does not include the divergence. At this point, it would be
measured before the knowledge graph build has created the entities of the new
documents. Read it from
GET /v1/orchestrate/{orchestration_id}
once the knowledge graph build has run. See
Where to read the divergence.
Adding extracts the text, creates an embedding, stores the document, assigns the
closest existing cluster, and adds the membership and similarity edges. It only
changes the corpus graph. To add the document to the knowledge graph, start
a knowledge graph build with
its file_id. AutoGraph resolves the partition itself, so you do not pass one,
see
Identifying documents for the knowledge graph.
The build only processes a document that is in a cluster with a strategy. Check
cluster_key and rag_partition_id in the response before you start it:
| Response | What it means | What to do |
|---|---|---|
| Both are set | The document is in a cluster with a strategy. | Pass its file_id to the knowledge graph build. |
cluster_key is set, rag_partition_id is empty | The cluster has no strategy yet. The build skips the document. | Run the RAG Strategizer for the category, then start the build. |
| Both are empty | No existing cluster is similar enough. The build skips the document. | Build the category again, see When to use IGU. |
The build lists skipped documents in unmatched_file_ids. If it skips all of
them, it returns 409, see
Telling the three 409 responses apart.
| Status Code | Meaning |
|---|---|
200 | The request has been processed. Check the success of every entry in results. |
400 | Empty or invalid batch, more than 100 files, a missing file_id on any entry, corpus not built, invalid category, duplicate names or IDs, file name mismatch, or the file could not be retrieved from the File Manager. |
401 | Authentication failed. |
403 | Access denied. |
409 | Another operation is currently changing the corpus. |
500 | Server error. |
503 | A required dependency is temporarily unavailable. |
HTTP example
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"files": [{
"doc_name": "new-contract.pdf",
"file_id": "<file-manager-file-id>"
}],
"category": "legal"
}' \
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}/v1/graph/insert
Remove documents
Removes documents from an existing category. The API calls this operation delete. The call is synchronous and does the whole removal itself. It cleans up the knowledge graph first, then the corpus graph. No Importer worker is involved, and there is no background job to wait for. The response is the final result.
Once the deletion has committed, AutoGraph also deletes the files of the removed documents, with all their versions, from the File Manager. To use a document again, upload it again. If deleting a file from the File Manager fails, the removal still reports success, and the file stays in the File Manager.
Use File Manager IDs in file_ids if you can, as they are stable. Otherwise,
provide doc_names, which are looked up in the requested category. You need to
provide at least one of the two lists.
Request
{
"file_ids": [
"<file-manager-file-id-1>",
"<file-manager-file-id-2>"
],
"category": "legal"
}
By file name instead:
{
"doc_names": ["old-contract.pdf"],
"category": "legal"
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file_ids | string[] | Conditional | The File Manager IDs of the documents to remove. Required unless you provide doc_names. |
doc_names | string[] | Conditional | The file names to look up in category. Required unless you provide file_ids. |
category | string | Conditional | The existing category the documents are in. Required unless the project has exactly one category. |
The batch is validated before anything is removed. If one of the documents is
invalid, the request returns 400 and nothing is removed.
Removing has no replica setting, because it has no workers. AutoGraph removes the knowledge graph data itself, with AQL queries, as part of this request. There are no Importer jobs to start or to scale, unlike a knowledge graph build.
Response
{
"results": [
{
"file_id": "<file-manager-file-id-1>",
"status": "LAYER2_DELETE_STATUS_SUCCESS",
"rag_partition_id": "legal_0_a",
"cluster_key": "cluster_legal_0",
"similarity_edges_removed": 8,
"divergence_score": 0.31,
"needs_reclustering": true
}
],
"affected_rag_partitions": ["legal_0_a"],
"removed_rag_partitions": [],
"layer3_results": [
{
"rag_partition_id": "legal_0_a",
"status": "LAYER3_DELETE_STATUS_SUCCESS",
"documents_removed": 1,
"chunks_removed": 8,
"entities_removed": 490,
"communities_removed": 0,
"semantic_units_removed": 0,
"edges_removed": 0
}
],
"overall_status": "DELETE_OVERALL_STATUS_COMMITTED",
"affected_cluster_ids": ["cluster_legal_0"],
"removed_cluster_ids": [],
"delete_id": "del_1711812345_a1b2c3d4"
}
The response reports the outcome of the complete deletion, knowledge graph
included. There is no importerOrchestration entry to poll, because a
standalone removal does not write one.
| Field | Meaning |
|---|---|
delete_id | The ID of this deletion, and the key of the concurrency lock it holds. Log it, so that you can correlate a 409 from a parallel call with the deletion that was holding the lock. |
overall_status | How the deletion as a whole ended, see the table below. |
results | The corpus graph result of every file, with the cluster and partition it belonged to, the number of similarity edges that were removed, and the divergence of the partition. If you removed documents by doc_names, file_id holds the file name. |
layer3_results | The knowledge graph result of the cleanup, per partition, with its status and the counts of what was removed. The counts are AutoGraph’s own AQL totals, not Importer figures. |
affected_rag_partitions / affected_cluster_ids | The partitions and clusters the deletion touched. |
removed_rag_partitions / removed_cluster_ids | The partitions and clusters that became empty and have been dropped. |
overall_status covers both stages and tells you whether you can retry.
| Value | Meaning |
|---|---|
DELETE_OVERALL_STATUS_COMMITTED | The deletion succeeded in both the knowledge graph and the corpus graph. |
DELETE_OVERALL_STATUS_ROLLED_BACK | Something failed, and the state from before the call has been restored. The documents are still in the graph, and it is safe to retry the same batch. |
DELETE_OVERALL_STATUS_FAILED | Something failed, and the restore itself was incomplete. Do not retry right away. Inspect the corpus and the knowledge graph before you call again. |
In field and status names, LAYER3 refers to the knowledge graph and LAYER2
to the corpus graph. The status of a layer3_results entry is one of
LAYER3_DELETE_STATUS_SUCCESS, LAYER3_DELETE_STATUS_FAILED,
LAYER3_DELETE_STATUS_NOT_ATTEMPTED, or LAYER3_DELETE_STATUS_UNSPECIFIED.
NOT_ATTEMPTED is a success case. It means there were no knowledge graph
collections for the deletion to work on, so there was nothing to clean up. The
corpus graph removal still goes ahead, and overall_status can still be
DELETE_OVERALL_STATUS_COMMITTED. Treat it as “nothing to do”, not as an error.
A failed deletion is rolled back. AutoGraph snapshots the knowledge graph
data before it removes anything. If the knowledge graph cleanup fails, the
snapshot is restored and the corpus graph does not change. If the corpus graph
removal then fails, the knowledge graph snapshots are restored as well. A
deletion that unwinds this way reports DELETE_OVERALL_STATUS_ROLLED_BACK, and
the same batch is safe to send again. Only DELETE_OVERALL_STATUS_FAILED means
the restore did not complete.
When the deletion commits, AutoGraph calculates the divergence of every
affected partition again and adds divergence_score and needs_reclustering
to the result of each file. The fields are only present if overall_status is
DELETE_OVERALL_STATUS_COMMITTED. A deletion that is rolled back or fails
reports no divergence.
The removed documents can be in other partitions than the ones a later
knowledge graph build processes, so keep needs_reclustering from this
response. See
Where to read the divergence.
| Status Code | Meaning |
|---|---|
200 | The deletion has been processed. Check overall_status and the status of every entry in results. |
400 | Missing or duplicate identifiers, a category that does not exist, a document that is not in the category, or a document in another category. Nothing is removed. |
401 | Authentication failed. |
403 | Access denied. |
409 | Another operation is currently changing the corpus. The delete_id of the deletion that holds the lock lets you correlate the two calls. |
500 | Server error. |
503 | A required dependency is temporarily unavailable. |
HTTP example
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"file_ids": ["<file-manager-file-id>"],
"category": "legal"
}' \
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}/v1/graph/delete
Replace documents
Replaces documents in an existing category with a new version. The API calls this operation update. The call is asynchronous.
Every document has to be in the requested category already. A single invalid document makes the whole batch fail before anything is changed.
Request
As when adding, every document is identified by its File Manager file_id.
Replacing takes no inline content. AutoGraph always reads the new version from
the File Manager. Upload the new version with
POST /_platform/filemanager/_db/{database}/rag-input
and pass the returned ID.
{
"files": [
{
"doc_name": "existing-contract.txt",
"file_id": "<file-manager-file-id>"
}
],
"category": "legal"
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
files | object[] | Yes | The documents to replace. The list cannot be empty and cannot contain duplicate doc_name or file_id values. Unlike adding, replacing has no per-request file cap. |
files[].doc_name | string | Yes | The file name of the document to replace. It has to match the File Manager file name of file_id. |
files[].file_id | string | Yes | The File Manager ID to get the new version from. Every entry needs one. If any entry is missing it, the whole batch is rejected with 400 and a message that lists the offending doc_name values. |
category | string | Conditional | The existing category the documents are in. Required unless the project has exactly one category. If you omit it in a project with a single category, the doc_name values are looked up in that category before anything is changed. |
POST /v1/graph/update still accepts the deprecated module field, which
is only honored when category is empty. Send category. Insert and delete
have no module field at all.
Immediate response
{
"update_id": "update_1721840400_a1b2c3d4",
"accepted": true,
"message": "Update started",
"results": []
}
accepted: true means that replacing has been validated and started, not that
it is done. Poll the importerOrchestration entry in the metadata of your
platform project, which
GET /_platform/acp/v1/project_by_name/{database}/{project}
returns, under projectMetadata.autographService.importerOrchestration. The
progress is in its message field, as a JSON string. Replacing goes through
the following phases. In their
names, L3 stands for the knowledge graph and L12 for the corpus graph:
DELETE_L3: AutoGraph removes the old knowledge graph data with AQL queries of its own. This is synchronous, and no Importer is involved.DELETE_L12: Removes the old document from the corpus graph.INSERT_L12: Inserts the new version into the corpus graph and assigns a cluster to it.DONE: Replacing has succeeded or failed.
Replacing uses the same logic as a standalone removal. It saves a snapshot of the knowledge graph data and removes that data before it changes the corpus graph.
The final message, parsed, looks like this:
{
"phase": "DONE",
"status": "completed",
"summary": "Updated 1 file(s) in Layers 1-2; run orchestrate to re-import Layer 3",
"files": [
{
"file": "existing-contract.txt",
"result": "updated",
"cluster": "cluster_legal_0",
"previous_cluster": "cluster_legal_1",
"partition": "legal_0_a",
"file_id": "<file-manager-file-id>"
}
]
}
Field in files[] | Meaning |
|---|---|
file | The document this result refers to. |
result | The result for this file, for example updated. |
cluster / previous_cluster | The cluster that is assigned to the new version, and the cluster the old version belonged to. |
partition | The knowledge graph partition of the new cluster. |
file_id | The File Manager ID of the new version. Use it in the file_ids of the knowledge graph build, and to add the document again with insert if replacing left it removed. |
error | Present only if this file failed. |
Only file and result are always present. The service adds every other key
conditionally, so read them as absent rather than empty when the value does not
apply.
Wait for phase: "DONE", not for status: "completed". The entry is shared
with knowledge graph builds and reorganizations. Right after you start
replacing, it can still hold the plain-text message of the previous build, such
as Orchestration completed: 11 succeeded, 0 failed, with
status: "completed".
Check the result of every file as well as the top-level status. A run can
report status: "completed" while an individual entry of files[] has an
error. Read the result and error of each file before you treat the
replacement as done.
The status does not include the divergence either. As when
adding, the new version only reaches the knowledge graph
through a knowledge graph build, so a value here would be measured before the
new entities exist. Read it from
GET /v1/orchestrate/{orchestration_id}
afterwards.
Replacing waits for the old knowledge graph data to be removed before it adds the new version, so that old and new knowledge graph data do not exist side by side. It is still not a database transaction.
If removing the old version succeeds but adding the new one fails, the document
stays removed. Fix the problem and add it again with POST /v1/graph/insert,
using the file_id from the entry of that file.
After the documents have been replaced, start a knowledge graph build to import the new version into the knowledge graph.
| Status Code | Meaning |
|---|---|
200 | Replacing has been validated and started. Poll for the result. |
400 | Empty or invalid batch, a missing file_id on any entry, a category that does not exist, or a document that is not in the category. |
401 | Authentication failed. |
403 | Access denied. |
409 | Another operation is currently changing the corpus. |
500 | Server error. |
503 | A required dependency is temporarily unavailable. |
HTTP example
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"files": [{
"doc_name": "existing-contract.txt",
"file_id": "<file-manager-file-id>"
}],
"category": "legal"
}' \
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}/v1/graph/update
Reorganize partitions
Rebuilds the communities of up to five partitions with the FullGraphRAG strategy from their current entities. The API calls this operation recluster. The call returns right away and reports whether each partition was taken up. This is not a queue position, see the response below. Reorganizing runs asynchronously, and only one partition is reorganized at a time.
Call this endpoint if needs_reclustering: true is reported for a partition and
you decide to rebuild its communities, which is expensive. AutoGraph never
reorganizes on its own. The flag is reported by
GET /v1/orchestrate/{orchestration_id},
by a removal once it has committed, and by the rags
profile of the partition. Adding and replacing do not report it. If a replaced
document moves to another cluster, the partition it left is reported nowhere
but in its rags profile. See
Where to read the divergence.
Only partitions with the FullGraphRAG strategy have communities that can be
rebuilt. A partition with the VectorRAG strategy has no Entities or
Communities, so there is nothing to rebuild, and such a partition is never
flagged.
The flag is set when the divergence_score is above the threshold,
which is 0.25 and not configurable. For how the divergence is calculated,
with examples, and how the values change over time, see
Divergence and Reorganization.
Plan reorganizing as a maintenance operation. It holds the single service-wide write lock for its entire run, which can take up to 3600 seconds, and blocks every corpus build and every addition, removal, and replacement until it is done. Partitions are reorganized one after another, so a request for several of them blocks changes for roughly the sum of their run times. Run it in a window where nothing else has to change the corpus.
Request
{
"partition_ids": ["legal_0_a", "engineering_0_a"]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
partition_ids | string[] | Yes | One or more rag_partition_id values, for example from the response of an addition, removal, or replacement. At least one non-empty ID is required, and at most five. Duplicates are ignored. |
Five partitions per request is a hard cap. A request that lists more than
five partition_ids is rejected with 400, and nothing is reorganized. Split
larger sets into several requests. They still run one at a time, so splitting
does not make them faster.
Response
{
"results": [
{
"rag_partition_id": "legal_0_a",
"accepted": true
},
{
"rag_partition_id": "engineering_0_a",
"accepted": true
}
]
}
| Field | Meaning |
|---|---|
results[].rag_partition_id | The partition this result refers to. |
results[].accepted | true if the request for this partition was taken up, or if it has been merged into a reorganization that is already running for this partition. It does not mean the work is queued, see the warning below. |
results[].error_message | Set if reorganizing could not be scheduled for this partition, for example because the ID is empty. |
accepted: true is not a queue position. Partitions are reorganized one at
a time, and there is no queue. A partition that has to wait for the write lock
makes about ten attempts, one second apart, to get it and then gives up. It
stays flagged with needs_reclustering, so that you can start it again
later. The response reports accepted: true for those partitions too,
so it does not tell you whether they ran.
Read the result of each partition from its rags strategy profile, not from
importerOrchestration. The importerOrchestration entry in your project
metadata shows the status of the job that has the lock, not the result of
each partition. A partition that gave up waiting for another reorganization
does not appear there at all. A partition that gave up waiting for any other
operation appears as failed. The rags node of the partition has the
reliable result.
Field on the rags node | After a successful reorganization |
|---|---|
needs_reclustering | Cleared to false |
last_reclustered_at | Set to the time of the run |
divergence_score | Reset to 0, and a new baseline is taken |
If needs_reclustering is still true and last_reclustered_at has not moved,
the partition was not reorganized, whether it failed or never got the lock.
Trigger it again. See Where the values are
stored.
| Status Code | Meaning |
|---|---|
200 | The request has been processed. Check the accepted value of every entry in results. |
400 | partition_ids is missing or empty, or it lists more than five partitions. |
401 | Authentication failed. |
403 | Access denied. |
500 | Server error. |
Notes:
- If a partition is already being reorganized, a second request is merged into it and does not start another Importer job.
- Reorganizing uses the same service-wide write lock as builds and as adding, removing, and replacing documents, so it cannot run at the same time as these operations.
- A reorganization that fails or is skipped does not clear
needs_reclustering. Start it again once the lock is free. - For a partition without entities, the job reports
0communities over0entities and finishes successfully without changing anything. A completed job does not mean that anything was rebuilt. Checklast_reclustered_atand theCommunitiesof the partition. - To learn what the Importer does while it reorganizes and how long it takes, see Importer Incremental Updates.
HTTP example
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"partition_ids": ["legal_0_a"]
}' \
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}/v1/graph/recluster
Build the knowledge graph for changed documents
Adding and replacing only change the corpus graph. To extract the entities and
relationships of the new content and add them to the knowledge graph, call
POST /v1/orchestrate with the file_ids of the added and replaced
documents. Only those documents are processed, in the partitions that contain
them. You do not name the partitions. AutoGraph finds them from the IDs.
Removing does not need this call. It removes the knowledge graph data itself.
Request
{
"project": "my_project",
"replicas": 1,
"file_ids": ["<added-or-replaced-file-id>"]
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
project | string | Yes | The platform project of the corpus. |
file_ids | string[] | Yes, for an incremental update | The file_id values from the response of an addition or the status of a replacement. A non-empty list skips the filter that otherwise only builds partitions that are not in the knowledge graph yet, so a partition that is already built can take the changed documents. |
replicas | integer | No | Number of Importer workers. 1 is enough for a handful of documents. |
categories | string[] | No | Narrows the IDs to these categories. Sending categories without file_ids does not work for an incremental update, see Identifying documents for the knowledge graph. |
For the complete list of parameters, the response, the credential handling, and
the three kinds of 409, see
Trigger Orchestration.
Identifying documents for the knowledge graph
The build identifies documents by file_id only, not by doc_name.
Adding and replacing both require a
File Manager file_id for every document, so you pass the same IDs on to the
build. IDs have the
form rag-input-{base64url(db:path)} and refer to a File Manager path, which is
why the project has to be built through the File Manager. Upload a document with
POST /_platform/filemanager/_db/{database}/rag-input
and pass the returned ID.
A non-empty file_ids is what keeps an already built partition eligible,
because it bypasses the filter that otherwise only builds partitions that are
not in the {project}_kg knowledge graph yet. Sending categories alone does
not bypass that filter, so it skips exactly the partitions that your addition or
replacement touched. If nothing else is stale, the request is rejected with a
409 and a Nothing to orchestrate message, see
Telling the three 409 responses apart.
Response
The call returns 202 with an orchestration_id and runs in the background.
IDs that matched no document are listed in unmatched_file_ids, which is only
reported on this response.
{
"orchestration_id": "orch_1711812345_a1b2c3d4",
"success": true,
"message": "Orchestration started",
"unmatched_file_ids": []
}
Poll
GET /v1/orchestrate/{orchestration_id}
until status leaves running. Each entry of jobs has the
divergence_score and needs_reclustering of its partition. jobs only lists
the partitions that the build processed. The partitions of removed documents,
and the partitions that replaced documents moved away from, are not in it. To
see all of them, read the rags profiles, for example with
GET /v1/rag-strategizer/strategy.
HTTP example
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"project": "my_project",
"replicas": 1,
"file_ids": ["<added-or-replaced-file-id>"]
}' \
https://<EXTERNAL_ENDPOINT>:8529/autograph/{service_id_postfix}/v1/orchestrate
For all steps in order, with example requests, see the Quick Start.
