# Arke API Operations Reference ## Base URL - https://api.arke.institute (Production) ## Authentication **JWT Bearer Token** (bearerAuth) - Header: `Authorization: Bearer ` - Supabase JWT token. Use `Authorization: Bearer `. Only for JWT tokens from Supabase auth - do NOT use Bearer with API keys. **API Key** (apiKeyAuth) - Header: `Authorization: ApiKey ` - API Key authentication. **Important:** Use `Authorization: ApiKey ` (NOT Bearer). Format: `ApiKey uk_xxx` (user keys) or `ApiKey ak_xxx` (agent keys). Using Bearer with API keys will fail. ## Global Headers **X-Arke-Network** (main | test), default: main Target network. Use `test` for isolated test data with II-prefixed IDs. **X-On-Behalf-Of** (string) User entity ID for service accounts acting on behalf of a user. Only valid with `role: service` authentication. ## Auth Levels - `[required]` - Must be authenticated with registered user - `[optional]` - Can be authenticated or anonymous - `[jwt-only]` - Valid JWT required but user may not be registered - (no tag) - No authentication needed Required fields in request bodies are marked with `*`. --- ## Auth & Users POST /auth/register [jwt-only] - Register new user Creates a user entity from JWT claims. Idempotent - returns existing user if already registered. --- **Permission:** `user:create` **Auth:** jwt-only GET /auth/whoami [required] - Get current identity Returns identity information for the authenticated caller. Accepts any valid credential type: - **JWT**: Returns user PI, email, name, and Supabase user ID - **User API Key (uk_)**: Returns user PI and key metadata - **Agent API Key (ak_)**: Returns agent PI, owner PI, and key metadata Use this endpoint to verify credentials are working or to retrieve the caller's entity PI. --- **Permission:** `auth:whoami` **Auth:** required GET /users/me [required] - Get current user Returns the authenticated user's entity. --- **Permission:** `user:view` **Auth:** required POST /users/me/keys [required] - Create API key body: {label:string, expires_in:integer} Creates a new API key for the authenticated user. The full key is only returned once - store it securely. --- **Permission:** `user:credentials` **Auth:** required GET /users/me/keys [required] - List API keys Lists all active API keys for the authenticated user. Returns prefixes only, not full keys. --- **Permission:** `user:credentials` **Auth:** required DELETE /users/me/keys/{prefix} [required] - Revoke API key path: prefix Revokes an API key by prefix. The key will be immediately invalid. --- **Permission:** `user:credentials` **Auth:** required GET /users/{id}/collections [optional] - List collections user has access to path: id query: predicate:string, limit:string, offset:string Returns all collections where the user has a role relationship (owner, editor, viewer, etc.). Queries GraphDB for collections with relationships pointing to this user where peer_type is 'user'. Results include the role predicate so clients know what access the user has to each collection. Supports filtering by predicate (role name) and pagination. --- **Permission:** `user:view` **Auth:** optional POST /users/me/search [required] - Search across user collections body: {query*:string, type:string, role:owner|editor|viewer, include_public:boolean, limit:integer, expand:preview|full|none} Performs semantic search across all collections the authenticated user has access to. ## Features - Searches all user's collections in parallel (up to 25) - Optionally includes public-domain entities - Filter by entity type or collection role - Results ranked by semantic relevance ## Performance - Collections are queried in parallel for speed - If user has more than 25 collections, queries first 25 (by created_at). Use role filter to narrow down. - Response includes metadata showing collections_queried vs collections_total ## Scoring - Results use cosine similarity scores (0-1) - Scores are comparable across collections ## Entity Expansion Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:execute` **Auth:** required ## Collections POST /collections [required] - Create a new collection body: {note:string, id:string, label*:string, description:string, display_image_url:string, use_roles_default:boolean, roles:object, properties:object, relationships:object[]} Creates a collection with the authenticated user as owner. **Default Roles:** By default (`use_roles_default: true`), collections include these standard roles: - `owner`: Full control including collection management - `editor`: Can modify entities but not collection settings - `viewer`: Read-only access - `public`: Public read access (`*:view`) **Customizing Roles:** Pass custom `roles` to override specific defaults while keeping others. For example, to make entities publicly invokable (for agents): ```json { "roles": { "public": ["*:view", "*:invoke"] } } ``` Set `use_roles_default: false` to define all roles from scratch. **Platform Requirement:** The `public` role with `*:view` is always automatically ensured, guaranteeing all collections are publicly readable. --- **Permission:** `collection:create` **Auth:** required GET /collections/catalog - List all collections for catalog/sitemap query: limit:number, offset:number Returns a paginated list of all collection IDs and their last update times. This endpoint is designed for sitemap generation and discovery services. It returns minimal data (just IDs and timestamps) for efficiency. **Lazy Population:** The catalog is populated as collections are created or updated. Newly created collections may take a moment to appear. **No Authentication Required:** This endpoint is public to allow crawlers and discovery services to access it. --- **Permission:** `catalog:list` **Auth:** none GET /collections/{id} [optional] - Get collection by ID path: id Returns a collection entity by ID. --- **Permission:** `collection:view` **Auth:** optional PUT /collections/{id} [required] - Update collection properties path: id query: validate_relationships:true|false body: {expect_tip*:string, note:string, properties:object, properties_remove:any, relationships_add:object[], relationships_remove:object[], label:string, description:string, display_image_url:string} Updates collection properties. Requires collection:update permission. **Relationship Target Validation:** By default, new relationship targets in `relationships_add` are validated to ensure they exist. Use `?validate_relationships=false` to skip this validation. Requests with more than 500 unique relationship targets are rejected when validation is enabled. --- **Permission:** `collection:update` **Auth:** required POST /collections/{id}/roles [required] - Add a new role path: id body: {role*:string, actions*:string[]} Adds a new role to the collection. Requires collection:manage permission. --- **Permission:** `collection:manage` **Auth:** required PUT /collections/{id}/roles/{role} [required] - Update role actions path: id, role body: {actions*:string[]} Updates the actions for an existing role. Requires collection:manage permission. --- **Permission:** `collection:manage` **Auth:** required DELETE /collections/{id}/roles/{role} [required] - Delete a role path: id, role Deletes a role from the collection. Requires collection:manage permission. --- **Permission:** `collection:manage` **Auth:** required GET /collections/{id}/members [optional] - List collection members path: id query: include_expired:true|false Returns all members of the collection grouped by type. By default, expired memberships are excluded. --- **Permission:** `collection:view` **Auth:** optional POST /collections/{id}/members [required] - Assign user to role path: id body: {user_id*:string, role*:string, expires_in:integer} Assigns a user to a role in the collection. Requires collection:manage permission. --- **Permission:** `collection:manage` **Auth:** required DELETE /collections/{id}/members/{userId} [required] - Remove user from role path: id, userId query: role*:string Removes a user from a role in the collection. Requires collection:manage permission. --- **Permission:** `collection:manage` **Auth:** required PUT /collections/{id}/root [required] - Set root entity path: id body: {expect_tip*:string, entity_id*:string} Links an entity as the root of this collection. **Prerequisites:** The entity must already have a 'collection' relationship pointing to this collection (typically set during entity creation via the `collection` field). **Recommended flow:** 1. Create entity with `collection` field set → entity is immediately protected 2. Call this endpoint to establish the root link from collection to entity This adds only the reverse relationship: - Collection → Entity (predicate: 'root') Requires collection:update permission on the collection. --- **Permission:** `collection:update` **Auth:** required GET /collections/{id}/entities [optional] - List entities in a collection path: id query: type:string, filter:string, limit:integer, offset:integer, expand:preview|full Returns entities belonging to this collection. Supports pagination and optional type filtering. Results are ordered by creation date (newest first). **Expansion Modes:** By default, returns lightweight summaries (pi, type, label, timestamps). Use the `expand` parameter to hydrate entity data from storage: - **`?expand=preview`**: Adds `preview` field with fresh lightweight data (label, truncated description/text, timestamps). ~5-10KB per entity. - **`?expand=full`**: Adds `entity` field with complete manifest (all properties, relationships, version history). ~20-50KB per entity. **Performance Note:** Expansion requires fetching each entity from storage. Limited to 100 entities per request. Use smaller `limit` values when expanding. --- **Permission:** `collection:view` **Auth:** optional GET /collections/{id}/entities/lookup [optional] - Lookup entities by label path: id query: label:string, type:string, limit:integer Fast (~5ms) lookup of entities by exact label and/or type within this collection. Uses a per-collection SQLite index for instant keyword queries. Complements semantic search for when you know the exact label. **Query Parameters:** - `label`: Exact label match (case-insensitive) - `type`: Filter by entity type - `limit`: Maximum results (default: 100, max: 1000) At least one of `label` or `type` should be provided for meaningful results. --- **Permission:** `collection:view` **Auth:** optional GET /collections/{id}/entities/search [optional] - Search entities by keyword or similarity path: id query: q:string, similar_to:string, type:string, limit:integer Search within a collection's entities using keyword matching or semantic similarity. **Keyword Search (q parameter):** - Fast (~5ms) using per-collection SQLite index - Case-insensitive substring match on entity labels **Semantic Similarity Search (similar_to parameter):** - Finds entities semantically similar to the given entity ID - Uses Pinecone vector similarity - Returns results with similarity scores One of `q` or `similar_to` must be provided. **Query Parameters:** - `q`: Search query (case-insensitive substring match) - `similar_to`: Entity ID to find similar items for - `type`: Filter by entity type - `limit`: Maximum results (default: 100, max: 1000) --- **Permission:** `collection:view` **Auth:** optional ## Entities POST /entities [required] - Create a new entity query: validate_relationships:true|false body: {note:string, id:string, type*:string, properties:object, relationships:object[], collection:string, sync_index:boolean} Creates a generic entity of any type. For type-specific validation, use type-specific endpoints. **Relationship Target Validation:** By default, all relationship targets are validated to ensure they exist. Use `?validate_relationships=false` to skip this validation (useful for migrations or when targets will be created shortly after). Requests with more than 500 unique relationship targets are rejected when validation is enabled. --- **Permission:** `entity:create` **Auth:** required POST /entities/batch [required] - Batch create entities query: validate_relationships:true|false body: {entities*:BatchCreateEntityItem[], default_collection:string} Creates multiple entities in a single request with bounded internal concurrency. Entities are created in chunks of 10 internally. Each entity follows the same permission model as single creates. Returns per-entity results. HTTP 201 if all succeed, 207 if some fail. **Max batch size:** 100 entities. **Relationship Target Validation:** By default, relationship targets are NOT validated for batch creates. This is because intra-batch references are common (entity A references entity B, both created in the same batch). Use `?validate_relationships=true` to enable validation if needed. --- **Permission:** `entity:create` **Auth:** required POST /entities/batch-get [optional] - Batch get entities by ID body: {ids*:string[]} Fetches multiple entities by ID in a single request. Permission is checked per-entity. Entities the caller cannot access are excluded from results and listed in `not_found`. **Use cases:** - Tree traversal: Expand multiple nodes at once - Workflow status: Fetch all log entries in a job - Relationship hydration: Fetch all peers of an entity **Max batch size:** 100 entities. --- **Permission:** `entity:view` **Auth:** optional GET /entities/{id} [optional] - Get entity by ID path: id query: expand:string, expand_limit:integer Returns any entity by ID. Permission check uses parent collection if entity belongs to one. **Relationship Expansion:** Use the `expand=relationships[:mode]` query parameter to hydrate relationship peer data: - **No expansion (default)**: Returns relationships with stored `peer_label`/`peer_type` (may be stale) ```json { "predicate": "contains", "peer": "01KDOC...", "peer_label": "Old Label" } ``` - **`?expand=relationships:preview`**: Adds `peer_preview` with fresh lightweight data (id, type, label, truncated description/text, timestamps) ```json { "predicate": "contains", "peer": "01KDOC...", "peer_label": "Old Label", "peer_preview": { "id": "01KDOC...", "type": "document", "label": "Updated Label", "description_preview": "This is a document with...", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-20T14:30:00Z" } } ``` - **`?expand=relationships:full`**: Adds `peer_entity` with complete entity manifest (all properties, relationships, version history) ```json { "predicate": "contains", "peer": "01KDOC...", "peer_label": "Old Label", "peer_entity": { "id": "01KDOC...", "cid": "bafyrei...", "type": "document", "properties": { "label": "Updated Label", "text": "..." }, "relationships": [...], "ver": 3, "created_at": "2025-01-15T10:00:00Z", "ts": 1737380000000, "edited_by": { "user_id": "01JUSER...", "method": "manual" } } } ``` **Performance:** Preview expansion is recommended for most use cases. Full expansion with many large entities can result in multi-MB payloads. **Expansion Limit:** Use `?expand_limit=N` to control maximum relationships expanded (1-500, default 100). When truncated, the response includes `_expansion_metadata` with counts. --- **Permission:** `entity:view` **Auth:** optional PUT /entities/{id} [required] - Update entity path: id query: validate_relationships:true|false body: {expect_tip*:string, note:string, properties:object, properties_remove:any, relationships_add:object[], relationships_remove:object[], sync_index:boolean} Updates an entity with merge semantics. **This is the recommended way to manage relationships.** - `relationships_add`: Upsert relationships (properties are merged if relationship exists) - `relationships_remove`: Remove by predicate/peer - `properties`: Deep merged with existing - `properties_remove`: Remove nested properties using nested object structure **properties_remove syntax:** - Top-level keys: `["field1", "field2"]` - Nested keys: `{ parent: { child: ["key_to_remove"] } }` - **Dot notation is NOT supported** - `["parent.child.key"]` will NOT work Example to remove `config.options.debug`: ```json { "properties_remove": { "config": { "options": ["debug"] } } } ``` Use `/relationships` only for bidirectional links updating two entities atomically. **Relationship Target Validation:** By default, new relationship targets in `relationships_add` are validated to ensure they exist. Use `?validate_relationships=false` to skip this validation. Requests with more than 500 unique relationship targets are rejected when validation is enabled. Note: entity:update on a collection requires collection:update permission. --- **Permission:** `entity:update` **Auth:** required DELETE /entities/{id} [required] - Delete entity path: id body: {expect_tip*:string, note:string, reason:string, sync_index:boolean} Soft-deletes an entity by creating a tombstone version. The entity can be restored later via POST /entities/:id/restore. Note: entity:delete on a collection requires collection:delete permission. --- **Permission:** `entity:delete` **Auth:** required GET /entities/{id}/preview [optional] - Get entity preview path: id Returns lightweight preview data for an entity. Useful for: - Link previews and hover cards - Relationship metadata freshness (vs stale peer_label) - AI context management (smaller payloads) - Search result enhancement Returns: id, type, label, collection_pi, description_preview (200 chars), text_preview (200 chars), timestamps. **Performance:** Single KV fetch, ~40-60ms response time, typically <1KB payload. --- **Permission:** `entity:view` **Auth:** optional GET /entities/{id}/tip - Get entity tip CID path: id Returns only the current manifest CID (tip) for an entity. Lightweight endpoint for CAS operations - single Durable Object lookup, no manifest fetch, no permission check. --- **Permission:** `entity:tip` **Auth:** none DELETE /entities/{id}/cascade [required] - Cascade delete entity and related entities path: id body: {expect_tip*:string, note:string, collection_id*:string, cascade_predicates*:string[], edited_by_filter:string, max_depth:integer, reason:string} Deletes an entity and all related entities matching predicate patterns within a scoped collection. **Permission Model:** - Requires `entity:delete` permission on the specified `collection_id` - Single permission check at the start (not per-entity) - Individual entity type permissions are NOT checked during cascade **Cascade Rules:** - `collection` predicate NEVER cascades (hard rule - protects collection structure) - Only entities in the specified collection are deleted - Entities outside the collection are skipped (reported in `skipped` array) **Predicate Patterns:** - `"child"` - exact match only - `"has_*"` - matches has_document, has_image, etc. - `"*_copy"` - matches file_copy, document_copy, etc. - `"*"` - matches ALL predicates (except collection) **Traversal:** - BFS traversal with parallel processing per depth layer - Max depth: 20 (default: 10) - Optional `edited_by_filter` to only delete entities created by a specific actor (useful for agent cleanup) **CAS Handling:** - Root entity uses the provided `expect_tip` - Child entities use re-fetch + single retry strategy - CAS conflicts are reported as skipped (not failures) **Response:** - Lists all deleted entities with their depth from root - Lists skipped entities with reasons - Provides summary statistics --- **Permission:** `entity:delete` **Auth:** required POST /entities/{id}/restore [required] - Restore deleted entity path: id body: {expect_tip*:string, note:string} Restores a deleted entity by finding the last non-deleted version and creating a new version from it. Note: entity:restore on a collection requires collection:restore permission. --- **Permission:** `entity:restore` **Auth:** required GET /entities/{id}/collection [optional] - Get entity collection path: id Returns the collection ID that this entity belongs to. Returns null if the entity is not in any collection. If the entity IS a collection, returns its own ID. --- **Permission:** `entity:view` **Auth:** optional GET /entities/{id}/tree [optional] - Get entity tree path: id query: depth:integer, collection:string, predicates:string, limit:integer Returns a hierarchical tree of entities reachable from the source entity. Use this to browse collections and folders without making multiple API calls. The tree follows relationship edges (optionally filtered by predicate) and returns a nested structure suitable for tree UI rendering. Query parameters: - `depth`: Max tree depth (1-4, default 2) - `collection`: Constrain to entities in this collection - `predicates`: Comma-separated predicates to follow (e.g., "contains") - `limit`: Max nodes to return (default 100) --- **Permission:** `entity:view` **Auth:** optional GET /entities/{id}/diff [optional] - Get diff between entity versions path: id query: from:string, to:string, format:semantic|patch Computes the difference between two versions of an entity. Query parameters: - `from`: CID of the "from" version (defaults to prev of "to" version) - `to`: CID of the "to" version (defaults to current tip) - `format`: Output format - "semantic" (default) or "patch" (RFC 6902) Modes: - No params: Compare current tip with its previous version - `to` only: Compare that version with its prev - `from` only: Compare from that version to current tip - Both: Compare any two versions For version 1 entities (no previous version), "from" is null and all content appears as added. --- **Permission:** `entity:view` **Auth:** optional GET /entities/{id}/permissions [required] - Get your permissions for an entity path: id Returns the list of actions you can perform on this entity. The response includes: - `allowed_actions`: Concrete actions you can perform (no wildcards) - `resolution`: How permissions were determined Resolution methods: - `collection`: Permissions from your role in the parent collection - `self`: You are checking your own user entity (self-ownership) - `open_season`: Entity is not in any collection (publicly accessible) Actions are filtered to only those relevant to the entity type: - For files: entity:* and file:* actions - For collections: entity:* and collection:* actions - etc. --- **Permission:** `entity:view` **Auth:** required POST /entities/{id}/content [required] - Upload content for any entity path: id query: key*:string, filename:string Uploads binary content for any entity type. Any entity can have content attached. **Request:** - Query param `key` (required): Version key for this content (e.g., "v1", "original", "thumbnail") - Query param `filename` (optional): Filename for Content-Disposition header on download - Header `Content-Type`: MIME type of the content (required) - Header `Content-Length`: Size for pre-upload validation (optional, max 500MB) - Body: Binary content (streaming supported) **Behavior:** - Streams content directly to R2 storage - Computes CID from content bytes - Updates entity with `properties.content` metadata - Re-uploading with same key overwrites the content - Creates a new entity version on each upload **Storage:** Content is stored at `{entity_id}/{cid}` in R2, enabling multiple versions per entity without overwriting. --- **Permission:** `entity:update` **Auth:** required GET /entities/{id}/content [optional] - Download content for any entity path: id query: key:string, cid:string Downloads binary content for any entity type. **Query Parameters:** - `key` (optional): Specific version key to download. Default: current content key from entity **Response Headers:** - `Content-Type`: MIME type of the content - `Content-Length`: Content size in bytes - `Content-Disposition`: attachment; filename="..." (if filename was set) **Streaming:** Response is streamed directly from R2 storage for efficient large file handling. --- **Permission:** `entity:view` **Auth:** optional DELETE /entities/{id}/content [required] - Remove content by key or CID path: id query: key:string, cid:string, expect_tip*:string Removes content metadata from entity. The actual file in R2 is preserved for version history. **Query Parameters (one required):** - `key`: Content key to remove - `cid`: Content CID to remove (alternative to key) - `expect_tip`: Current entity tip CID for CAS protection (required) **Behavior:** - Removes the content entry from the content map - R2 file is NOT deleted (preserved for version history) - Returns 404 if content not found - If CID matches multiple keys, returns error (specify key instead) **Examples:** ``` DELETE /entities/{id}/content?key=thumbnail&expect_tip=bafyrei... DELETE /entities/{id}/content?cid=bafyrei...&expect_tip=bafyrei... ``` --- **Permission:** `entity:update` **Auth:** required PATCH /entities/{id}/content [required] - Rename content key path: id body: {old_key*:string, new_key*:string, expect_tip*:string} Renames a content key without moving the underlying R2 file. Since content is stored by CID, renaming a key is a metadata-only operation. The R2 file remains at `{entityId}/{cid}` and is unaffected. **Use Cases:** - Renaming `v1` to `original` for semantic clarity - Reorganizing content keys without re-uploading **Note:** This does not change the filename property. Use entity update for that. --- **Permission:** `entity:update` **Auth:** required POST /entities/{id}/content/upload-url [required] - Get presigned URL for direct upload path: id body: {cid*:string, content_type*:string, size*:integer} Returns a presigned URL for direct upload to R2 storage, bypassing the API worker. **When to use:** - Files larger than 5MB (avoids streaming through API worker) - Client has reliable network (single PUT request to R2) - Parallel uploads (get multiple URLs, upload in parallel, complete sequentially) **Flow:** 1. Compute CID client-side (hash the file content) 2. Call this endpoint with the CID to get presigned URL 3. PUT file directly to the returned URL (include Content-Type header) 4. Call POST /{id}/content/complete to finalize (this is where key/filename are specified) **Parallel uploads:** For multiple files, steps 1-3 can be parallelized. Only step 4 (complete) must be sequential with CAS retry to update entity metadata atomically. **Presigned URL:** - Valid for 15 minutes - R2 path is content-addressed: {entityId}/{cid} - Must include Content-Type header matching the request **Security:** The presigned URL is the access control - it's signed and time-limited. Without a valid URL from this endpoint, uploads to R2 will fail. **Note:** The CID is computed client-side and trusted. For guaranteed integrity, use the direct upload endpoint (POST /{id}/content) which computes CID server-side. --- **Permission:** `entity:update` **Auth:** required POST /entities/{id}/content/complete [required] - Complete presigned URL upload path: id body: {key*:string, cid*:string, size*:integer, content_type*:string, filename:string, expect_tip*:string} Finalizes a presigned URL upload by updating entity metadata. **Prerequisites:** 1. Called POST /{id}/content/upload-url to get presigned URL 2. Uploaded file directly to R2 via presigned URL 3. Computed CID client-side **Request:** - `key`: Same key used in upload-url request - `cid`: Content-addressed identifier computed by client - `size`: Actual file size in bytes - `content_type`: MIME type of uploaded content - `expect_tip`: Current entity tip for CAS protection **Behavior:** - Verifies file exists in R2 at expected location - Updates entity with content metadata - Creates new entity version **CID Trust:** The client-provided CID is trusted without server verification. For guaranteed integrity, use direct upload (POST /{id}/content). --- **Permission:** `entity:update` **Auth:** required GET /entities/{id}/attestation [optional] - Get latest attestation path: id Returns the Arweave attestation for the current (latest) version of an entity. Returns 202 Accepted if the attestation upload is still pending. --- **Permission:** `entity:view` **Auth:** optional ## Versions GET /versions/{id} [optional] - List version history path: id query: limit:integer, from:string Returns version metadata for an entity (newest first). Use pagination for entities with many versions. --- **Permission:** `entity:view` **Auth:** optional GET /versions/manifest/{cid} [optional] - Get manifest by CID path: cid Returns the full manifest for any version by its CID. Permission is checked against the entity ID in the manifest. --- **Permission:** `entity:view` **Auth:** optional GET /versions/{id}/{ver}/attestation [optional] - Get version attestation path: id, ver Returns the Arweave attestation for a specific version of an entity. --- **Permission:** `entity:view` **Auth:** optional ## Graph & Query POST /graph/paths [required] - Find paths between entities query: expand:preview|full|none body: {source_ids*:string[], target_ids*:string[], max_depth:integer, direction:outgoing|incoming|both, limit:integer} Find shortest paths between source and target entity sets. Returns all paths up to the limit (default 100). Use this when you know both endpoints and want to discover how they connect - for example, finding the chain of relationships between a person and a document. **Entity Expansion (default: preview):** - Omit `expand` or use `?expand=preview` - Lightweight previews for all path entities - `?expand=full` - Complete entity manifests (use with caution) - `?expand=none` - Disable expansion (graph metadata only) **Performance Warning:** 100 paths can reference 400-800 unique entities, adding 5-10s latency with expansion. **Recommendations:** - Use `limit: 10-20` when using expansion - Prefer preview over full - Use `expand=none` for large result sets, then fetch specific entities separately --- **Permission:** `graph:query` **Auth:** required POST /graph/reachable [required] - Find reachable entities (exhaustive) query: expand:preview|full|none body: {source_ids*:string[], target_type*:string, max_depth:integer, direction:outgoing|incoming|both, limit:integer} Find all entities of a specific type reachable from source entities within N hops. Returns up to 100 results by default. **When to use this vs POST /query:** This endpoint returns exhaustive, unranked results - all reachable entities up to the limit. Use `POST /query` when you want relevance-ranked results combining semantic similarity with graph structure. Use this endpoint when you need comprehensive graph exploration from known entity IDs. **Target Expansion (default: preview):** - Omit `expand` or use `?expand=preview` - Lightweight target previews - `?expand=full` - Complete target manifests - `?expand=none` - Disable expansion (graph metadata only) **Performance:** With 100 targets, expansion adds ~1s. --- **Permission:** `graph:query` **Auth:** required GET /graph/entity/{id} [required] - Get entity from graph path: id query: expand:preview|full|none Get entity details with all relationships from the graph database. Unlike the entity manifest, this includes both outgoing and incoming relationships - showing not just what this entity links to, but also what links to it. **Peer Expansion (default: preview):** - Omit `expand` or use `?expand=preview` - Lightweight peer previews - `?expand=full` - Complete peer manifests - `?expand=none` - Disable expansion (graph metadata only) **Performance:** With 50 peers, expansion adds ~500ms. --- **Permission:** `graph:query` **Auth:** required POST /query [required] - Execute Argo query body: {path*:string, k:integer, k_explore:integer, collection:string, expand:none|preview|full, filter:object} Execute an Argo DSL query for path-based graph traversal with relevance ranking. ## When to Use This Endpoint | Endpoint | Use Case | |----------|----------| | `POST /query` | Semantic search + graph traversal with **relevance-ranked** results (default k=25) | | `POST /graph/reachable` | **Exhaustive** graph exploration from known entities (default limit=100) | | `POST /graph/paths` | Find all shortest paths between two entity sets | This endpoint combines semantic similarity scores with path length to rank results. For exhaustive graph traversal without ranking, use the `/graph/*` endpoints directly. ## Query Syntax ``` [SCOPE_PREFIX] ENTRY_POINT [ENTRY_FILTER] [-[RELATION]{DEPTH}-> TARGET_FILTER]... ``` ## Scope Prefixes Control where semantic search looks for entry points. Default is discovery mode. | Prefix | Description | Example | |--------|-------------|---------| | (none) | **Discovery mode** (default) - find relevant collections, then search within each | `"medical notes"` | | `@:collections` | Search for collections themselves | `@:collections "columbia archives"` | | `@:collection(id)` | Search within a specific collection | `@:collection(01JCOLL123) "meeting"` | | `@:discover` | Explicit discovery mode | `@:discover "research papers"` | | `@:public` | Search public domain only | `@:public "orphaned data"` | **Note:** Graph traversal (hops) is always cross-collection regardless of scope. ### Entry Points | Syntax | Description | Example | |--------|-------------|---------| | `"text"` | Semantic search | `"george washington"` | | `@id` | Exact entity ID | `@01KE4ZY69F9R40E88PK9S0TQRQ` | | `type:X` | All entities of type | `type:person` | | `type:X ~ "text"` | Semantic search within type | `type:person ~ "physician"` | ### Edges (Hops) | Syntax | Direction | |--------|-----------| | `-[*]->` | Outgoing | | `<-[*]-` | Incoming | | `<-[*]->` | Both | | `-[*]{,4}->` | Variable depth (1-4) | ### Examples ``` "george washington" # Discovery mode (default) @:collections "columbia university" # Find collections @:collection(01JCOLL123) "faculty meeting" # Within specific collection @:discover "alice" -[*]{,2}-> type:person # Discover, then traverse @01KE4ZY... -[*]{,2}-> type:person # From exact entity ``` ## Entity Expansion Control how much entity data is included in results via the `expand` parameter. | Value | Description | Use Case | |-------|-------------|----------| | (omitted) | **Preview** (default) - lightweight preview data | Best balance of detail and payload size | | `preview` | Same as omitted - lightweight preview data | Explicit preview mode | | `full` | Complete entity manifest | When you need all properties, relationships, versions | | `none` | No expansion - Pinecone metadata only | Fastest response, smallest payload | **Preview mode** includes: label, truncated description/text (200 chars), created_at, updated_at. **Full mode** includes: all properties, relationships, version info, CID. Both result entities and path step entities are expanded. --- **Permission:** `query:execute` **Auth:** required ## Search POST /search/similar/collections [required] - Find similar collections body: {id*:string, limit:number, refresh:boolean, expand:preview|full|none} Find collections that are semantically similar to a given collection. Uses the collection's weighted centroid vector (combination of description and entity embeddings) to find related collections. **Entity Expansion:** Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:similar` **Auth:** required POST /search/similar/items [required] - Find similar items across collections body: {id*:string, collection_id*:string, limit:number, tier1_limit:number, tier2_limit:number, include_same_collection:boolean, refresh:boolean, expand:preview|full|none} Find entities that are semantically similar to a given entity, searching across multiple collections. This performs a two-tier search: 1. First finds collections similar to the entity's collection 2. Then searches within each collection for similar items 3. Aggregates and ranks results with diversity weighting **Entity Expansion:** Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:similar` **Auth:** required POST /search/collections [required] - Search collections by text body: {query*:string, limit:number, types:string[], filter:object, expand:preview|full|none} Search for collections using semantic text search. Use this endpoint to discover collections about a topic. Results are ranked by semantic similarity to your query. **Entity Expansion:** Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:query` **Auth:** required POST /search/tools [required] - Search tools (Rhiza workflows and Klados actions) by text body: {query*:string, limit:number, filter:object, expand:preview|full|none, scope:official|all, type:rhiza|klados|all} Search for Rhiza workflows and Klados actions using semantic text search. **Tool Types:** - **Rhiza**: Complete workflows that orchestrate multiple actions - **Klados**: Individual actions that can be composed into workflows Use the `type` parameter to filter by tool type, or search both with `type: "all"` (default). **Official Tools (Default):** By default, this endpoint searches only the official Arke tools collection. These tools are pre-approved, actively maintained, and tested for security and reliability. **All Tools:** Set `scope: "all"` to search network-wide. This is not recommended as results may include duplicates, outdated tools, or unapproved implementations. Results are ranked by semantic similarity to your query based on tool descriptions and capabilities. **Entity Expansion:** By default, tool search returns **full entity manifests** (including schemas, inputs, outputs, etc.) to make discovery results immediately useful. - **`expand: "full"` (default)**: Complete tool manifests with all properties - **`expand: "preview"`**: Lightweight previews (id, type, label, description_preview, timestamps) - **`expand: "none"`**: Search metadata only (fastest) --- **Permission:** `search:query` **Auth:** required POST /search/entities [required] - Search entities within collection(s) body: {collection_id:string, collection_ids:string[], query*:string, limit:number, types:string[], filter:object, per_collection_limit:number, expand:preview|full|none} Search for entities within one or more collections using semantic text search. Provide either `collection_pi` for a single collection or `collection_ids` for multiple collections (searched in parallel). Use `per_collection_limit` to ensure result diversity when searching multiple collections. **Entity Expansion:** Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:query` **Auth:** required POST /search/discover [required] - Discover entities across all collections body: {query*:string, limit:number, types:string[], filter:object, collection_limit:number, per_collection_limit:number, expand:preview|full|none} Two-step discovery search: first finds relevant collections, then searches within them. Use this endpoint when you don't know which collections to search. The system will: 1. Find collections semantically related to your query 2. Search within each collection in parallel 3. Aggregate and rank results across all collections Great for exploration and AI agents navigating the network. **Entity Expansion:** Use `expand` in the request body to fetch entity data inline with search results: - **`expand: "preview"` (default)**: Adds `entity_preview` with fresh lightweight data (id, type, label, timestamps) - **`expand: "full"`**: Adds `entity` with complete manifest including all properties and relationships - **`expand: "none"`**: Returns search metadata only (fastest, lowest bandwidth) Preview mode is recommended for most use cases. Full expansion can result in large payloads. Gracefully handles deleted or inaccessible entities (returns results without expansion data). --- **Permission:** `search:query` **Auth:** required ## Chat POST /chat [required] - Send chat message body: {messages*:ChatMessage[]} Send a message to the Arke chat agent and receive a streaming response. The agent can execute Arke API operations on your behalf using the authenticated user's permissions. ## Headers - `X-Chat-ID`: Optional. Specify to continue an existing chat session. If omitted, a new session is created. ## Response Format The response is a Server-Sent Events (SSE) stream in AI SDK v5 UIMessage format. Stream chunks include text deltas, tool calls, and usage information. ## Token Usage Tracking Usage information is included at the end of the stream in the format: ```json {"type":"message_delta","delta":{"usage":{"input_tokens":123,"output_tokens":456}}} ``` ## Storage Limits - **Single message**: 2 MB max (returns 413 with code `MESSAGE_TOO_LARGE`) - **Chat database**: 10 GB max (returns 507 with code `CHAT_STORAGE_FULL`) - **LLM context**: ~128K tokens (returns stream error) --- **Permission:** `chat:send` **Auth:** required GET /chat/sessions [required] - List user chats Returns a paginated list of the authenticated user's chats, sorted newest-first. Only returns chats owned by the authenticated user. Anonymous chats are not indexed. Query parameters: - `limit`: Max chats to return (1-100, default 20) - `offset`: Number of chats to skip for pagination (default 0) --- **Permission:** `chat:view` **Auth:** required GET /chat/sessions/{id} [required] - Get chat session Get information about a chat session including message history. Only the session owner can view their chat sessions. --- **Permission:** `chat:view` **Auth:** required DELETE /chat/sessions/{id} [required] - Delete chat session Delete a chat session. Only the session owner can delete it. --- **Permission:** `chat:delete` **Auth:** required ## Events & Attestations GET /permissions - Get permission system metadata Returns all registered actions, verbs, types, verb implications, wildcard patterns, and default roles. This endpoint is useful for: - Building dynamic role editors - Understanding available permissions - Validating actions client-side All data is auto-generated from the actual permission system, so it's always in sync with the code. --- **Permission:** `permissions:read` **Auth:** none GET /events - List entity change events query: since:integer, until:integer, cursor:integer, limit:integer, network:main|test Returns a cursor-based list of entity change events for client synchronization. **Cursor options (mutually exclusive):** - `since`: Forward pagination - events with id > since, ascending order (oldest first) - `until`: Backward pagination - events with id < until, descending order (newest first) - `cursor`: Deprecated alias for `until` (backwards compatibility) - Neither: Returns newest events, descending order **Examples:** ``` # Poller catching up (forward from last processed) GET /events?since=500&limit=100 → { events: [501, 502, ...], has_more: true, cursor: 600 } # Next page: GET /events?since=600 # Browsing history (backward from a point) GET /events?until=500&limit=100 → { events: [499, 498, ...], has_more: true, cursor: 400 } # Next page: GET /events?until=400 # Latest events (default) GET /events?limit=100 → { events: [1500, 1499, ...], has_more: true, cursor: 1401 } ``` **Sync flow:** 1. Initial: `GET /events` → get newest events, save highest `id` as high-water mark 2. Poll: `GET /events?since=X` → forward from high-water mark to get new events 3. Backfill: `GET /events?until=X` → backward to get older events until `has_more=false` **Event data:** - `id`: Auto-increment ID (use as cursor) - `entity_id`: Entity ID that changed - `cid`: New manifest CID - `ts`: ISO timestamp Events are ephemeral (30-day rolling window) - for full sync, use snapshots. --- **Permission:** `events:list` **Auth:** none GET /attestations/head - Get chain head Returns the latest Arweave attestation transaction ID (network head). --- **Permission:** `attestation:view` **Auth:** none GET /attestations/verify/{tx} - Verify attestation path: tx Fetches an attestation from Arweave and verifies the CID matches the manifest content. This is a public endpoint - anyone can verify attestations. --- **Permission:** `attestation:verify` **Auth:** none ## Other GET /ops-reference - Get LLM-friendly API reference Returns a condensed, plain-text API operations reference optimized for LLM consumption. This endpoint provides the same information as the OpenAPI spec but in a format that: - Uses ~80% fewer tokens than the full OpenAPI JSON - Preserves full endpoint descriptions - Organizes operations by category - Marks required fields with `*` suffix - Includes auth requirements inline **Format example:** ``` ## Collections POST /collections [required] - Create a new collection body: {label*:string, description:string} Creates a collection with the authenticated user as owner. ``` Use this for injecting API knowledge into LLM system prompts. GET /updates/queue/{id} [required] - Get update queue status for entity path: id Returns the status of the additive update queue for a specific entity. **Use Cases:** - Diagnose why additive updates may not have been applied - Check for pending, processing, or failed updates - Monitor queue processing progress **Response includes:** - Counts by status (pending, processing, failed) - Detailed items with actor, relationships count, error messages, attempts --- **Permission:** `entity:view` **Auth:** required POST /updates/additive [required] - Queue additive updates (fire-and-forget) body: {updates*:AdditiveUpdateItem[]} Queues additive updates for multiple entities. Returns 202 Accepted immediately. **Fire-and-Forget Semantics:** - Updates are queued and processed asynchronously - CAS conflicts are handled internally with exponential backoff retry - Client does not need to retry on conflicts **Additive-Only Operations:** - `properties`: Deep merged with existing properties - `relationships_add`: Upsert semantics (add new or merge properties) - `properties_remove` and `relationships_remove` are **NOT supported** - use `PUT /entities/:id` for removals **Per-Actor Versioning:** - Multiple updates from the same actor are merged before applying (one version) - Updates from different actors create separate versions (preserves audit trail) **Use Cases:** - Many workers adding relationships to a shared parent entity - High-volume indexing where multiple sources enrich the same entity - Any scenario where ordering doesn't matter and CAS conflicts are expected **Max batch size:** 100 updates per request. --- **Permission:** `entity:update` **Auth:** required POST /kladoi [required] - Create a klados body: {note:string, id:string, label*:string, endpoint*:string, actions_required*:string[], accepts*:object, produces*:object, collection*:string, description:string, input_schema:object, properties:object, relationships:object[]} Creates a new klados entity. Requires klados:create permission in the target collection. --- **Permission:** `klados:create` **Auth:** required GET /kladoi/{id} [optional] - Get klados by ID path: id Returns a klados entity by ID. --- **Permission:** `klados:view` **Auth:** optional PUT /kladoi/{id} [required] - Update klados path: id body: {expect_tip*:string, note:string, properties:object, properties_remove:any, relationships_add:object[], relationships_remove:object[], label:string, description:string, endpoint:string, actions_required:string[], status:development|active|disabled, accepts:object, produces:object, input_schema:object} Updates a klados. Requires klados:update permission. **Endpoint verification rules:** - Changing `endpoint` clears `endpoint_verified_at` and resets status to 'development' - Setting `status: 'active'` requires `endpoint_verified_at` to be set --- **Permission:** `klados:update` **Auth:** required POST /kladoi/{id}/invoke [required] - Invoke a klados path: id body: {target_entity:string, target_entities:string[], target_collection*:string, job_collection:string, input:object, expires_in:integer, confirm:boolean, rhiza:object} Invoke a klados to perform work on a target entity. **Two-phase interaction:** 1. `confirm: false` (default) - preview permissions that will be granted 2. `confirm: true` - execute the klados The klados receives temporal (time-limited) permissions on the target collection. --- **Permission:** `klados:invoke` **Auth:** required POST /kladoi/{id}/verify [required] - Verify klados endpoint ownership path: id body: {confirm:boolean} Verify that you control the klados's endpoint URL. This is required before activating a klados. **Two-phase flow:** 1. Call without `confirm` to get a verification token 2. Deploy `/.well-known/arke-verification` endpoint returning the token 3. Call with `confirm: true` to complete verification **Verification endpoint format:** Your endpoint must return JSON: ```json { "verification_token": "vt_xxx...", "klados_id": "01xxx..." } ``` --- **Permission:** `klados:manage` **Auth:** required POST /kladoi/{id}/reinvoke [required] - Reinvoke a failed klados job path: id body: {log_id*:string, expires_in:integer} Retry a failed klados job by extracting the original invocation from the error log and re-invoking. **Requirements:** - The log file must be a klados_log with `status: error` - The log must contain `received.invocation` with the original request - The klados ID in the log must match the route parameter **What happens:** 1. Extracts the original `KladosRequest` from `received.invocation.request` 2. Generates a new `job_id` 3. Updates `rhiza_context.parent_logs` to point to the failed log (for audit trail) 4. Invokes the klados with fresh `expires_at` The new job's log will point back to the failed log, preserving the complete retry history. --- **Permission:** `klados:invoke` **Auth:** required POST /kladoi/{id}/keys [required] - Create API key for klados path: id body: {label:string, expires_in_days:integer} Creates an API key for the klados. The full key is only returned once. --- **Permission:** `klados:manage` **Auth:** required GET /kladoi/{id}/keys [required] - List API keys for klados path: id Lists all active API keys for the klados (without the actual key values). --- **Permission:** `klados:manage` **Auth:** required DELETE /kladoi/{id}/keys/{prefix} [required] - Revoke API key path: id, prefix Revokes an API key for the klados. --- **Permission:** `klados:manage` **Auth:** required POST /rhizai [required] - Create a rhiza workflow body: {note:string, id:string, label*:string, version*:string, entry*:string, flow*:object, collection*:string, description:string, properties:object, relationships:object[]} Creates a new rhiza workflow entity. Requires rhiza:create permission in the target collection. **Flow validation:** - Entry klados must be in flow - All flow targets must be in flow (or external rhiza refs) - All paths must terminate (done: true or external target) - No cycles allowed --- **Permission:** `rhiza:create` **Auth:** required GET /rhizai/{id} [optional] - Get rhiza by ID path: id Returns a rhiza entity by ID. --- **Permission:** `rhiza:view` **Auth:** optional PUT /rhizai/{id} [required] - Update rhiza path: id body: {expect_tip*:string, note:string, properties:object, properties_remove:any, relationships_add:object[], relationships_remove:object[], label:string, description:string, version:string, entry:string, flow:object, status:development|active|disabled} Updates a rhiza. Requires rhiza:update permission. --- **Permission:** `rhiza:update` **Auth:** required POST /rhizai/{id}/invoke [required] - Invoke a rhiza workflow path: id body: {target_entity:string, target_entities:string[], target_collection*:string, input:object, expires_in:integer, confirm:boolean} Invoke a rhiza workflow to process a target entity. **Two-phase interaction:** 1. `confirm: false` (default) - preview permissions for all kladoi 2. `confirm: true` - execute the workflow All kladoi in the workflow receive temporal (time-limited) permissions. --- **Permission:** `rhiza:invoke` **Auth:** required GET /rhizai/{id}/jobs/{job_id}/status [optional] - Get workflow job status path: id, job_id Returns the status of a workflow job. Reads klados_log entries from the job collection to compute: - Overall status (pending/running/done/error) - Progress counters - Current running kladoi - Error summaries --- **Permission:** `rhiza:view` **Auth:** optional POST /rhizai/{id}/jobs/{job_id}/resume [required] - Resume failed workflow path: id, job_id body: {error_codes:string[]} Resume a workflow by re-invoking failed kladoi that have retryable errors. Only retryable errors are resumed. Non-retryable errors are skipped. --- **Permission:** `rhiza:invoke` **Auth:** required