Collections
Creating and managing collections -- the permission boundaries of the Arke network.
Overview
Collections are the fundamental permission boundary in Arke. Every entity belongs to at least one collection, and a user's role in the collection determines what they can do with entities inside it.
Creating a Collection
POST /collections
Authorization: Bearer <token>
{
"label": "Whaling Archives",
"description": "Melville's manuscripts and maritime records"
}Response (201 Created):
{
"id": "01JCOL...",
"cid": "bafyrei...",
"type": "collection",
"properties": {
"label": "Whaling Archives",
"description": "Melville's manuscripts and maritime records",
"roles": {
"owner": ["*:view", "*:update", "*:create", "collection:update", "collection:manage"],
"editor": ["*:view", "*:update", "*:create"],
"viewer": ["*:view"],
"public": ["*:view"]
},
"_profile_version": "v1"
},
"relationships": [
{ "predicate": "public", "peer": "*", "peer_type": "wildcard" },
{ "predicate": "owner", "peer": "01JUSER...", "peer_type": "user" }
],
"ver": 1,
"created_at": "2025-01-15T12:00:00.000Z",
"ts": "2025-01-15T12:00:00.000Z",
"edited_by": { "user_id": "01JUSER...", "method": "manual" }
}The creating user automatically becomes the owner. If no custom roles are provided, the collection uses default roles (owner, editor, viewer, public).
Optional Fields
| Field | Type | Description |
|---|---|---|
id | string | Custom entity ID (generated if not provided) |
label | string | Required. Display name for the collection |
description | string | Collection description (max 2000 chars) |
display_image_url | string | URL for collection display image |
roles | object | Custom role definitions (see below) |
properties | object | Additional properties to store |
relationships | array | Initial relationships |
note | string | Version note |
Getting a Collection
GET /collections/:id
Authorization: Bearer <token> # Optional for public collectionsResponse (200 OK):
{
"id": "01JCOL...",
"cid": "bafyrei...",
"type": "collection",
"properties": {
"label": "Whaling Archives",
"description": "Melville's manuscripts and maritime records",
"roles": { ... }
},
"relationships": [...],
"ver": 1,
"created_at": "2025-01-15T12:00:00.000Z",
"ts": "2025-01-15T12:00:00.000Z",
"edited_by": { ... }
}Updating a Collection
PUT /collections/:id
Authorization: Bearer <token>
{
"expect_tip": "bafyrei...",
"label": "The Pequod's Archive",
"description": "Updated description"
}Response (200 OK):
{
"id": "01JCOL...",
"cid": "bafynew...",
"type": "collection",
"properties": { ... },
"relationships": [...],
"ver": 2,
"created_at": "2025-01-15T12:00:00.000Z",
"ts": "2025-01-15T12:30:00.000Z",
"edited_by": { ... },
"prev_cid": "bafyrei..."
}The expect_tip field is required for optimistic concurrency control (CAS). If the current collection version doesn't match, you'll receive a 409 Conflict.
Update Fields
| Field | Type | Description |
|---|---|---|
expect_tip | string | Required. Current CID for CAS check |
label | string | Updated display name |
description | string | Updated description |
display_image_url | string | Updated display image URL |
properties | object | Additional properties to merge |
properties_remove | object | Properties to remove |
relationships_add | array | Relationships to add |
relationships_remove | array | Relationships to remove |
note | string | Version note |
Note: Roles cannot be updated via this endpoint. Use the role management endpoints below.
Managing Roles
Roles define what actions members can perform. Each collection has a set of role definitions that map role names to action arrays.
Default Roles
| Role | Actions | Description |
|---|---|---|
owner | *:view, *:update, *:create, collection:update, collection:manage | Full control including collection management |
editor | *:view, *:update, *:create | Can modify entities but NOT collection settings |
viewer | *:view | Read-only access |
public | *:view | Public access (required for all collections) |
Action Format
Actions follow the resource:verb pattern:
entity:view- View entitiesentity:create- Create entitiesentity:update- Update entitiescollection:manage- Manage roles and members*:view- View any resource type (wildcard)file:*- Any action on files (type wildcard)
Add a Role
POST /collections/:id/roles
Authorization: Bearer <token>
{
"role": "harpooner",
"actions": ["*:view", "*:update", "*:create"]
}Response (201 Created):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"roles": {
"owner": [...],
"editor": [...],
"viewer": [...],
"public": [...],
"harpooner": ["*:view", "*:update", "*:create"]
},
"ver": 3
}Update a Role
PUT /collections/:id/roles/:role
Authorization: Bearer <token>
{
"actions": ["*:view", "*:update", "*:create", "entity:delete"]
}Response (200 OK):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"roles": { ... },
"ver": 4
}Delete a Role
DELETE /collections/:id/roles/:role
Authorization: Bearer <token>Response (200 OK):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"roles": { ... },
"ver": 5
}Deleting a role also removes all member assignments for that role. The public role cannot be deleted (required for all collections).
Managing Members
Members are users assigned to roles within a collection. Role assignments are stored as relationships on the collection entity.
List Members
GET /collections/:id/members
Authorization: Bearer <token>
# Optional query parameters:
# ?include_expired=true - Include expired membershipsResponse (200 OK):
{
"collection_id": "01JCOL...",
"members": [
{
"userId": "01JUSER...",
"role": "owner",
"userLabel": "Captain Ahab",
"granted_at": "2025-01-15T12:00:00.000Z",
"granted_by": "01JUSER...",
"is_expired": false
},
{
"userId": "01JUSER2...",
"role": "editor",
"userLabel": "Ishmael",
"expires_at": "2025-02-15T12:00:00.000Z",
"granted_at": "2025-01-15T12:00:00.000Z",
"granted_by": "01JUSER...",
"is_expired": false
}
],
"groups": [],
"wildcards": [
{ "role": "public" }
]
}Assign a Member to a Role
POST /collections/:id/members
Authorization: Bearer <token>
{
"user_id": "01JUSER...",
"role": "editor"
}Response (201 Created):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"member_added": {
"user_id": "01JUSER...",
"role": "editor",
"granted_at": "2025-01-15T12:00:00.000Z",
"granted_by": "01JACTOR..."
},
"ver": 6
}Temporary Access
You can grant time-limited access using the expires_in field (seconds):
POST /collections/:id/members
Authorization: Bearer <token>
{
"user_id": "01JUSER...",
"role": "editor",
"expires_in": 86400
}Response (201 Created):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"member_added": {
"user_id": "01JUSER...",
"role": "editor",
"expires_at": "2025-01-16T12:00:00.000Z",
"granted_at": "2025-01-15T12:00:00.000Z",
"granted_by": "01JACTOR..."
},
"ver": 6
}Remove a Member from a Role
DELETE /collections/:id/members/:userId?role=editor
Authorization: Bearer <token>The role query parameter is required to specify which role assignment to remove.
Response (200 OK):
{
"id": "01JCOL...",
"cid": "bafynew...",
"prev_cid": "bafyold...",
"member_removed": {
"user_id": "01JUSER...",
"role": "editor"
},
"ver": 7
}Setting a Root Entity
Collections can have a "root" entity that serves as the entry point for the collection's content hierarchy. This is useful for folder structures or document trees.
PUT /collections/:id/root
Authorization: Bearer <token>
{
"expect_tip": "bafyrei...",
"entity_id": "01JFOLDER..."
}Prerequisites: The entity must already have a collection relationship pointing to this collection (typically set during entity creation via the collection field).
Response (200 OK):
{
"id": "01JCOL...",
"cid": "bafynew...",
"type": "collection",
"properties": { ... },
"relationships": [
...,
{ "predicate": "root", "peer": "01JFOLDER...", "peer_type": "folder" }
],
"ver": 8,
"created_at": "...",
"ts": "...",
"edited_by": { ... },
"prev_cid": "bafyrei...",
"root_entity_id": "01JFOLDER..."
}Listing Collection Entities
Retrieve all entities belonging to a collection via the graph database.
GET /collections/:id/entities
Authorization: Bearer <token>
# Query parameters:
# ?type=document - Filter by entity type
# ?limit=100 - Max entities (default: 1000, max: 10000)
# ?offset=0 - Pagination offset
# ?expand=preview - Include lightweight previews
# ?expand=full - Include full entity manifests (max 100 entities)Response (200 OK):
{
"collection_id": "01JCOL...",
"entities": [
{
"pi": "01JDOC...",
"type": "document",
"label": "Chapter 1: Loomings",
"created_at": "2025-01-15T12:00:00.000Z",
"updated_at": "2025-01-15T14:00:00.000Z"
}
],
"pagination": {
"offset": 0,
"limit": 100,
"count": 1,
"has_more": false
}
}Expansion Modes
By default, returns lightweight summaries from GraphDB.
?expand=preview: Addspreviewfield with fresh lightweight data (label, truncated description/text, timestamps). ~5-10KB per entity.?expand=full: Addsentityfield with complete manifest (all properties, relationships, version history). ~20-50KB per entity. Limited to 100 entities.
Finding Entities by Label
Collections maintain a local index of entity labels for fast lookup. These endpoints query the collection's index directly (not the graph database), providing sub-10ms response times.
Exact Lookup
Find entities with an exact label match (case-insensitive):
GET /collections/:id/entities/lookup?label=Captain%20Ahab
Authorization: Bearer <token>
# Query parameters:
# ?label=... - Required. Exact label to match
# ?type=... - Filter by entity type
# ?limit=10 - Max results (default: 10)Response (200 OK):
{
"entities": [
{
"pi": "01JCHAR...",
"type": "character",
"label": "Captain Ahab",
"cid": "bafyrei...",
"updated_at": "2025-01-15T14:00:00.000Z"
}
],
"count": 1
}Use this when you know the exact entity label.
Keyword Search
Find entities with labels containing a substring (case-insensitive):
GET /collections/:id/entities/search?q=ahab
Authorization: Bearer <token>
# Query parameters:
# ?q=... - Required. Search term (substring match)
# ?type=... - Filter by entity type
# ?limit=20 - Max results (default: 20)Response (200 OK):
{
"entities": [
{
"pi": "01JCHAR...",
"type": "character",
"label": "Captain Ahab",
"cid": "bafyrei...",
"updated_at": "2025-01-15T14:00:00.000Z"
},
{
"pi": "01JCHAP...",
"type": "chapter",
"label": "Chapter 36: Ahab's Leg",
"cid": "bafyrei...",
"updated_at": "2025-01-15T14:00:00.000Z"
}
],
"count": 2
}Use this for partial matches or keyword discovery.
Which Endpoint to Use
| Scenario | Endpoint | Example |
|---|---|---|
| Know exact label | /entities/lookup | "Get entity labeled 'Captain Ahab'" |
| Partial match | /entities/search | "Find entities with 'ahab' in label" |
| Browse all entities | /entities | "List all entities in collection" |
| Full-text content search | Semantic search API | "Find content about whale hunting" |
The lookup and search endpoints query the collection's local index. For full-text semantic search across entity content, use the Search API.
Permission Summary
| Endpoint | Required Action |
|---|---|
POST /collections | collection:create |
GET /collections/:id | collection:view |
PUT /collections/:id | collection:update |
POST /collections/:id/roles | collection:manage |
PUT /collections/:id/roles/:role | collection:manage |
DELETE /collections/:id/roles/:role | collection:manage |
GET /collections/:id/members | collection:view |
POST /collections/:id/members | collection:manage |
DELETE /collections/:id/members/:userId | collection:manage |
PUT /collections/:id/root | collection:update |
GET /collections/:id/entities | collection:view |
GET /collections/:id/entities/lookup | collection:view |
GET /collections/:id/entities/search | collection:view |
Public vs Private
All collections have a public role with minimum *:view permissions. This is a platform requirement. The public wildcard relationship ({ predicate: "public", peer: "*", peer_type: "wildcard" }) makes the collection discoverable by anyone on the network.
To restrict visibility, you can remove the public wildcard relationship or customize the public role's actions (while maintaining minimum platform requirements).