Skip to main content

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
Important

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.

Where does USER_ID come from?

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"
}
Graph View

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:

TypeDescriptionExample
EXPERIENCEThings that happened"First day of college"
BELIEFWhat you think is true"Education changes lives"
ENTITYPeople, places, things"Dr. Sarah Chen - my mentor"
GOALWhat 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

LevelDescriptionWhen to Use
DATAJust a factOrdinary information
CONTEXTUseful backgroundImportant but not defining
IDENTITYStory-constitutiveCan't explain yourself without it

If omitted, meaning_level defaults to CONTEXT.

Start with DATA or 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:

FieldDescription
idUnique proposal identifier
statuspending, approved, or rejected
typeWhat kind of change is proposed (e.g. CREATE_NODE)
canonical_dataThe structured data you submitted
created_atWhen the proposal was created

What Happens Next?

  1. User sees proposal in their OfSelf dashboard
  2. User reviews the proposed changes
  3. User approves or rejects
  4. If approved, the node is created in their vault
  5. 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:

  1. Learn about Nodes - The core data structure
  2. Understand Proposals - How apps modify user data
  3. Set up Webhooks - Get real-time notifications