Your First API Call
Let's make your first API call to OfSelf! We'll create a proposal for a node in a user's identity graph.
Prerequisites
- A registered third-party app with an API key
- A user who has authorized your app
Third-party apps must use API Key + X-User-ID authentication. User JWT authentication (login with email/password) is exclusively for app.ofself.ai and is NOT available to third-party developers.
The X-User-ID value used throughout this guide is the UUID you receive in the authorization callback when a user approves your app. See the Authentication guide for how to obtain it.
Step 1: Authenticate with Your API Key
Use your API key and the authorized user's ID:
curl -X GET "https://api.ofself.ai/api/v1/nodes" \
-H "X-API-Key: ofs_tp_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \
-H "X-User-ID: user-123"
Step 2: Create a Proposal
Third-party apps create proposals that users approve. Create a proposal for a new node:
curl -X POST "https://api.ofself.ai/api/v1/proposals" \
-H "X-API-Key: ofs_tp_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \
-H "X-User-ID: user-123" \
-H "Content-Type: application/json" \
-d '{
"title": "Add new experience",
"description": "Proposing to add a new experience node",
"type": "CREATE_NODE",
"graph_view": "identity",
"canonical_data": {
"entities": [{
"title": "Hello OfSelf!",
"value": "This is my first node created via proposal.",
"node_type": "EXPERIENCE",
"meaning_level": "DATA",
"graph_view": "identity"
}]
}
}'
Response:
{
"id": "proposal-uuid",
"status": "PENDING",
"created_at": "2024-01-15T10:30:00Z"
}
The graph_view field defaults to "identity", meaning nodes appear in the user's identity graph by default. You can explicitly set "graph_view": "neutral" for nodes you don't want in the identity view.
Node Types
Node types depend on the meaning level:
IDENTITY layer — must use one of the 4 Paradigm types:
| Type | Description | Example |
|---|---|---|
| EXPERIENCE | Things that happened | "First day of college" |
| BELIEF | What you think is true | "Education changes lives" |
| ENTITY | People, places, things | "Dr. Sarah Chen - my mentor" |
| GOAL | What you're working toward | "Learn Spanish by 2025" |
DATA / CONTEXT layers — accept any free-text node_type:
document,note,recipe,meeting_notes,log,bookmark, etc.
Meaning Levels
| Level | Description | When to Use |
|---|---|---|
| DATA | Just a fact | Ordinary information |
| CONTEXT | Useful background | Important but not defining |
| IDENTITY | Story-constitutive | Can't explain yourself without it |
If omitted, meaning_level defaults to CONTEXT.
When unsure, start with DATA or CONTEXT meaning level. You can elevate to IDENTITY later.
Using Python
Using requests
import requests
API_KEY = "ofs_tp_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
USER_ID = "user-123" # User who authorized your app
headers = {
"X-API-Key": API_KEY,
"X-User-ID": USER_ID,
"Content-Type": "application/json"
}
# Create a proposal
proposal = requests.post(
"https://api.ofself.ai/api/v1/proposals",
headers=headers,
json={
"title": "Add new experience",
"description": "Proposing to add a new experience node",
"type": "CREATE_NODE",
"graph_view": "identity",
"canonical_data": {
"entities": [{
"title": "Hello OfSelf!",
"value": "This is my first node created via proposal.",
"node_type": "EXPERIENCE",
"meaning_level": "DATA",
"graph_view": "identity"
}]
}
}
).json()
print(f"Created proposal: {proposal['id']} (status: {proposal['status']})")
Using the SDK
pip install ofself
from ofself import OfSelfClient
# Initialize with API key
client = OfSelfClient(api_key="ofs_tp_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy")
# List nodes for an authorized user
nodes = client.nodes.list(user_id="user-123")
for node in nodes['items']:
print(f" - {node['title']} ({node['node_type']})")
Using JavaScript
const API_KEY = "ofs_tp_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
const USER_ID = "user-123"; // User who authorized your app
// Create a proposal
const proposalResponse = await fetch("https://api.ofself.ai/api/v1/proposals", {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"X-User-ID": USER_ID,
"Content-Type": "application/json"
},
body: JSON.stringify({
title: "Add new experience",
description: "Proposing to add a new experience node",
type: "CREATE_NODE",
graph_view: "identity",
canonical_data: {
entities: [{
title: "Hello OfSelf!",
value: "This is my first node created via proposal.",
node_type: "EXPERIENCE",
meaning_level: "DATA",
graph_view: "identity"
}]
}
})
});
const proposal = await proposalResponse.json();
console.log(`Created proposal: ${proposal.id} (status: ${proposal.status})`);
Understanding the Response
When you create a proposal, you get back:
| Field | Description |
|---|---|
id | Unique proposal identifier |
status | pending, approved, or rejected |
type | What kind of change is proposed (e.g. CREATE_NODE) |
canonical_data | The structured data you submitted |
created_at | When the proposal was created |
What Happens Next?
- User sees proposal in their OfSelf dashboard
- User reviews the proposed changes
- User approves or rejects
- If approved, the node is created in their vault
- Your app receives webhook (if configured) notifying of the change
Error Handling
Always handle potential errors:
from ofself import OfSelfClient
from ofself.exceptions import (
AuthenticationError,
PermissionDenied,
NotFoundError,
RateLimitError
)
client = OfSelfClient(api_key="your-key")
try:
nodes = client.nodes.list(user_id="user-123")
except AuthenticationError:
print("Invalid API key")
except PermissionDenied:
print("User hasn't authorized your app")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except Exception as e:
print(f"Unexpected error: {e}")
Next Steps
Now that you've made your first API call:
- Learn about Nodes - The core data structure
- Understand Proposals - How apps modify user data
- Set up Webhooks - Get real-time notifications