| title | Solid API Reference | ||||||
|---|---|---|---|---|---|---|---|
| description | Complete API reference for VisionFlow Solid Pod integration including endpoints, authentication, WebSocket protocol, and examples. | ||||||
| category | reference | ||||||
| tags |
|
||||||
| updated-date | 2025-12-29 | ||||||
| difficulty-level | intermediate |
Version: 1.0
Base URL: http://localhost:9090/solid
WebSocket URL: ws://localhost:9090/solid/ws
Complete API reference for VisionFlow's Solid Pod integration.
All Solid API requests require Nostr NIP-98 authentication.
Use the session token obtained from Nostr login:
Authorization: Bearer <nostr_session_token>The session token is obtained during the Nostr authentication flow and stored in localStorage as nostr_session_token.
For operations requiring fresh signatures, use NIP-98 HTTP Auth:
Authorization: Nostr <base64_encoded_signed_event>The signed event must include:
{
"kind": 27235,
"created_at": 1703859000,
"tags": [
["u", "http://localhost:9090/solid/pods"],
["method", "POST"],
["payload", "sha256_hash_of_body"]
],
"content": "",
"pubkey": "npub1...",
"sig": "signature..."
}Event Fields:
| Tag | Description | Required |
|---|---|---|
u |
Full request URL | Yes |
method |
HTTP method (GET, POST, PUT, DELETE) | Yes |
payload |
SHA-256 hash of request body (hex) | If body present |
Timing Requirements:
created_atmust be within 60 seconds of server time- Events are single-use (replay protection)
Create a new Solid Pod for the authenticated user.
Request:
POST /solid/pods HTTP/1.1
Authorization: Nostr <signed_event>
Content-Type: application/json
{
"name": "my-knowledge-base",
"template": "visionflow-default"
}Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Pod identifier (alphanumeric, hyphens) |
template |
string | No | Pod template to use |
Available Templates:
| Template | Description |
|---|---|
visionflow-default |
Full VisionFlow structure with memories, ontologies |
minimal |
Basic profile and preferences only |
agent-focused |
Optimised for agent memory storage |
ontology-contributor |
Focus on ontology proposals |
Response (201 Created):
{
"url": "/pods/npub1abc.../my-knowledge-base/",
"webId": "https://visionflow.example/id/npub1abc.../profile/card#me",
"created": "2025-12-29T10:30:00Z",
"template": "visionflow-default",
"containers": [
"/pods/npub1abc.../my-knowledge-base/profile/",
"/pods/npub1abc.../my-knowledge-base/agent-memories/",
"/pods/npub1abc.../my-knowledge-base/ontologies/",
"/pods/npub1abc.../my-knowledge-base/graphs/"
]
}Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_POD_NAME |
Pod name contains invalid characters |
| 401 | UNAUTHORIZED |
Invalid or expired authentication |
| 409 | POD_EXISTS |
Pod with this name already exists |
| 500 | POD_CREATION_FAILED |
Internal server error during creation |
Check if a Pod exists for the authenticated user.
Request:
GET /solid/pods/check?name=my-knowledge-base HTTP/1.1
Authorization: Bearer <session_token>Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Pod name to check |
Response (200 OK):
{
"exists": true,
"url": "/pods/npub1abc.../my-knowledge-base/",
"created": "2025-12-29T10:30:00Z",
"size": 102400,
"resourceCount": 42
}Response if not found (200 OK):
{
"exists": false
}List all Pods owned by the authenticated user.
Request:
GET /solid/pods HTTP/1.1
Authorization: Bearer <session_token>Response (200 OK):
{
"pods": [
{
"name": "my-knowledge-base",
"url": "/pods/npub1abc.../my-knowledge-base/",
"created": "2025-12-29T10:30:00Z",
"template": "visionflow-default"
},
{
"name": "work-ontologies",
"url": "/pods/npub1abc.../work-ontologies/",
"created": "2025-12-28T14:20:00Z",
"template": "ontology-contributor"
}
],
"totalCount": 2,
"storageUsed": 512000,
"storageQuota": 104857600
}Delete a Pod and all its contents.
Request:
DELETE /solid/pods/my-knowledge-base HTTP/1.1
Authorization: Nostr <signed_event>Response (204 No Content): Empty body on success.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED |
Invalid authentication |
| 403 | FORBIDDEN |
Cannot delete another user's Pod |
| 404 | POD_NOT_FOUND |
Pod does not exist |
All LDP operations follow the Linked Data Platform specification.
Retrieve a resource or container listing.
Request:
GET /solid/pods/npub1abc.../agent-memories/episodic/memory-001 HTTP/1.1
Authorization: Bearer <session_token>
Accept: application/ld+jsonHeaders:
| Header | Value | Description |
|---|---|---|
Accept |
application/ld+json |
JSON-LD format (default) |
Accept |
text/turtle |
Turtle RDF format |
Accept |
application/n-triples |
N-Triples format |
Response (200 OK):
HTTP/1.1 200 OK
Content-Type: application/ld+json
ETag: "abc123"
Link: <http://www.w3.org/ns/ldp#Resource>; rel="type"{
"@context": {
"@vocab": "https://visionflow.example/ns/memory#"
},
"@id": "/pods/npub1abc.../agent-memories/episodic/memory-001",
"@type": "EpisodicMemory",
"timestamp": "2025-12-29T10:30:00Z",
"content": "User explored design patterns in the software ontology"
}Container Listing (for directories):
{
"@context": {
"ldp": "http://www.w3.org/ns/ldp#"
},
"@id": "/pods/npub1abc.../agent-memories/episodic/",
"@type": "ldp:Container",
"ldp:contains": [
{ "@id": "memory-001" },
{ "@id": "memory-002" },
{ "@id": "memory-003" }
]
}Replace a resource with new content.
Request:
PUT /solid/pods/npub1abc.../profile/preferences HTTP/1.1
Authorization: Nostr <signed_event>
Content-Type: application/ld+json
If-Match: "abc123"{
"@context": { "@vocab": "https://visionflow.example/ns/prefs#" },
"@type": "UserPreferences",
"theme": "dark",
"language": "en-GB",
"memoryRetention": "1y"
}Headers:
| Header | Description |
|---|---|
If-Match |
ETag for optimistic concurrency (recommended) |
If-None-Match |
* to only create if not exists |
Response (200 OK or 201 Created):
{
"@id": "/pods/npub1abc.../profile/preferences",
"updated": "2025-12-29T11:00:00Z"
}Create a new resource in a container.
Request:
POST /solid/pods/npub1abc.../agent-memories/episodic/ HTTP/1.1
Authorization: Nostr <signed_event>
Content-Type: application/ld+json
Slug: memory-2025-12-29-001{
"@context": { "@vocab": "https://visionflow.example/ns/memory#" },
"@type": "EpisodicMemory",
"timestamp": "2025-12-29T10:30:00Z",
"content": "Session started - user authenticated via Nostr"
}Headers:
| Header | Description |
|---|---|
Slug |
Suggested resource name (server may modify) |
Link |
<http://www.w3.org/ns/ldp#Resource>; rel="type" for RDF resources |
Response (201 Created):
HTTP/1.1 201 Created
Location: /pods/npub1abc.../agent-memories/episodic/memory-2025-12-29-001Remove a resource.
Request:
DELETE /solid/pods/npub1abc.../agent-memories/episodic/old-memory HTTP/1.1
Authorization: Nostr <signed_event>Response (204 No Content): Empty body on success.
Apply partial updates using SPARQL UPDATE or N3 Patch.
Request (SPARQL UPDATE):
PATCH /solid/pods/npub1abc.../profile/card HTTP/1.1
Authorization: Nostr <signed_event>
Content-Type: application/sparql-updatePREFIX foaf: <http://xmlns.com/foaf/0.1/>
DELETE { <#me> foaf:name ?old }
INSERT { <#me> foaf:name "New Display Name" }
WHERE { <#me> foaf:name ?old }Response (200 OK):
{
"success": true,
"triples": {
"added": 1,
"removed": 1
}
}Connect to the Solid WebSocket endpoint for real-time notifications.
Connection URL:
ws://localhost:9090/solid/ws?token=<session_token>
Handshake:
GET /solid/ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Protocol: solid-0.1
Subscribe to changes on a resource or container.
Request:
{
"type": "sub",
"resource": "/pods/npub1abc.../agent-memories/",
"recursive": true
}Fields:
| Field | Type | Description |
|---|---|---|
type |
string | "sub" |
resource |
string | Resource or container URL |
recursive |
boolean | Include child resources (containers only) |
Response (ack):
{
"type": "ack",
"resource": "/pods/npub1abc.../agent-memories/",
"subscriptionId": "sub-001"
}Cancel a subscription.
Request:
{
"type": "unsub",
"resource": "/pods/npub1abc.../agent-memories/"
}Response:
{
"type": "ack",
"unsubscribed": "/pods/npub1abc.../agent-memories/"
}Notification when a subscribed resource changes.
Message:
{
"type": "pub",
"resource": "/pods/npub1abc.../agent-memories/episodic/memory-005",
"activity": "created",
"actor": "npub1abc...",
"timestamp": "2025-12-29T11:30:00Z",
"container": "/pods/npub1abc.../agent-memories/episodic/"
}Activity Types:
| Activity | Description |
|---|---|
created |
New resource created |
updated |
Resource content modified |
deleted |
Resource removed |
moved |
Resource relocated |
All errors follow a consistent format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error description",
"details": {
"field": "Additional context"
}
}
}| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED |
401 | Missing or invalid authentication |
FORBIDDEN |
403 | Insufficient permissions |
NOT_FOUND |
404 | Resource does not exist |
CONFLICT |
409 | Resource already exists or version conflict |
INVALID_RDF |
400 | Malformed RDF content |
INVALID_CONTENT_TYPE |
415 | Unsupported media type |
PRECONDITION_FAILED |
412 | If-Match/If-None-Match failed |
QUOTA_EXCEEDED |
507 | Storage quota exceeded |
SERVER_ERROR |
500 | Internal server error |
import { nostrAuth } from './nostrAuthService';
// 1. Get authentication
const token = nostrAuth.getSessionToken();
const user = nostrAuth.getCurrentUser();
// 2. Create the memory object
const memory = {
"@context": {
"@vocab": "https://visionflow.example/ns/memory#",
"xsd": "http://www.w3.org/2001/XMLSchema#"
},
"@type": "EpisodicMemory",
"timestamp": new Date().toISOString(),
"sessionId": "session-" + Date.now(),
"content": "User navigated to the design patterns section",
"entities": [
{ "@id": "http://example.org/ontology#FactoryPattern" }
],
"importance": 0.6
};
// 3. POST to create
const response = await fetch('/solid/pods/' + user.npub + '/agent-memories/episodic/', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/ld+json',
'Slug': 'memory-' + Date.now()
},
body: JSON.stringify(memory)
});
if (response.status === 201) {
const location = response.headers.get('Location');
console.log('Memory created at:', location);
}const ws = new WebSocket(`ws://localhost:9090/solid/ws?token=${token}`);
ws.onopen = () => {
// Subscribe to all memory changes
ws.send(JSON.stringify({
type: 'sub',
resource: `/pods/${user.npub}/agent-memories/`,
recursive: true
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'pub') {
console.log(`Memory ${msg.activity}: ${msg.resource}`);
// Trigger UI update
refreshMemoryList();
}
};const query = `
PREFIX vf: <https://visionflow.example/ns/memory#>
SELECT ?memory ?content ?importance
WHERE {
?memory a vf:EpisodicMemory ;
vf:content ?content ;
vf:importance ?importance .
FILTER(?importance > 0.7)
}
ORDER BY DESC(?importance)
LIMIT 5
`;
const response = await fetch(`/solid/pods/${user.npub}/agent-memories/`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/sparql-query',
'Accept': 'application/json'
},
body: query
});
const results = await response.json();
console.log('Important memories:', results.results.bindings);- Solid Integration Guide - Getting started guide
- Nostr Authentication - Authentication details
- REST API Complete - Full REST API reference
- WebSocket API - WebSocket protocol reference
Last Updated: 2025-12-29 Version: 1.0 Maintainer: VisionFlow Documentation Team