Documentation Index
Fetch the complete documentation index at: https://mintlify.com/openmined/syft-space/llms.txt
Use this file to discover all available pages before exploring further.
Syft Space uses bearer token authentication for API requests.
Authentication methods
There are two authentication methods depending on your use case:
Local authentication
SyftHub authentication
Used when accessing your own Syft Space instance directly.How it works:
- Login with email and password
- Receive JWT bearer token
- Include token in subsequent requests
Best for:
- Managing your own Space
- Development and testing
- Direct API access
Used when querying published endpoints through the SyftHub marketplace.How it works:
- Authenticate with SyftHub
- Receive satellite token
- Use token to query any published endpoint
Best for:
- Querying endpoints on other Spaces
- Marketplace integration
- Multi-Space queries
Local authentication
Register account
Create a new account:
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "secure-password",
"tenant_name": "my-space"
}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"tenant_name": "my-space",
"created_at": "2024-01-15T10:30:00Z"
}
Login
Get an access token:
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "secure-password"
}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600
}
Using the token
Include the token in the Authorization header:
curl -X GET http://localhost:8080/api/v1/datasets/ \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Headers:
Authorization: Bearer <access_token>
Tokens expire after 1 hour (3600 seconds). Login again to get a new token.
SyftHub authentication
Get satellite token
Authenticate with SyftHub to get a satellite token:
Login or register
Create an account or login with existing credentials
Generate token
Navigate to Settings → API Tokens and create a new token
Copy token
Copy the satellite token to use in your requests
Query with satellite token
Use the satellite token to query any published endpoint:
curl -X POST https://space-url.com/api/v1/endpoints/my-docs/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <satellite-token>" \
-d '{
"messages": [
{"role": "user", "content": "What are the main topics?"}
]
}'
How it works:
- You send query with satellite token
- Space validates token with SyftHub
- SyftHub confirms identity and permissions
- Space processes query if authorized
- Usage is tracked for billing/analytics
Local JWT token
Local tokens are JSON Web Tokens (JWT) with this structure:
{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"email": "user@example.com",
"tenant_name": "my-space",
"exp": 1705318200,
"iat": 1705314600
},
"signature": "..."
}
Claims:
email - User’s email address
tenant_name - Tenant context
exp - Expiration timestamp (Unix)
iat - Issued at timestamp (Unix)
Satellite token
Satellite tokens are opaque tokens issued by SyftHub:
Prefix indicates environment:
sat_live_ - Production
sat_test_ - Testing/sandbox
Security best practices
- Never commit tokens to version control
- Store in environment variables or secret manager
- Use different tokens for different environments
- Rotate tokens regularly
Protect tokens in transit
- Always use HTTPS in production
- Never send tokens in URLs or query parameters
- Use Authorization header, not cookies
- Validate SSL certificates
def make_request(url, token):
response = requests.get(
url,
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 401:
# Token expired, re-authenticate
token = login()
response = requests.get(
url,
headers={"Authorization": f"Bearer {token}"}
)
return response
Revoke compromised tokens
If a token is compromised:
- Logout from the application
- Change your password
- Generate new tokens
- Update applications using the old token
Multi-tenancy
For programmatic access to multiple tenants:
Authorization: Bearer <token>
X-Tenant-Name: my-other-space
The X-Tenant-Name header overrides the tenant in the JWT token. Only works if:
- Token belongs to a user with access to the specified tenant
- User has appropriate permissions in that tenant
Example:
curl -X GET http://localhost:8080/api/v1/datasets/ \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "X-Tenant-Name: another-space"
Testing authentication
Verify your token is working:
# Get current user info
curl -X GET http://localhost:8080/api/v1/auth/me \
-H "Authorization: Bearer <token>"
Response:
{
"email": "user@example.com",
"tenant_name": "my-space",
"created_at": "2024-01-15T10:30:00Z"
}
If authentication fails:
{
"detail": "Could not validate credentials"
}
Error handling
401 Unauthorized
Token is missing, invalid, or expired:
{
"detail": "Not authenticated"
}
Solutions:
- Include Authorization header
- Login to get new token
- Check token format (“Bearer YOUR_TOKEN”)
403 Forbidden
Token is valid but lacks permissions:
{
"detail": "Insufficient permissions"
}
Solutions:
- Check tenant access
- Verify endpoint policies
- Contact Space owner for access
Code examples
Python with token refresh
import requests
from datetime import datetime, timedelta
class AuthenticatedClient:
def __init__(self, base_url: str, email: str, password: str):
self.base_url = base_url
self.email = email
self.password = password
self.token = None
self.token_expiry = None
def login(self):
response = requests.post(
f"{self.base_url}/api/v1/auth/login",
json={"email": self.email, "password": self.password}
)
response.raise_for_status()
data = response.json()
self.token = data["access_token"]
self.token_expiry = datetime.now() + timedelta(
seconds=data["expires_in"]
)
def get_headers(self):
# Refresh token if expired or expiring soon
if not self.token or datetime.now() >= self.token_expiry - timedelta(minutes=5):
self.login()
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
def get(self, path: str):
response = requests.get(
f"{self.base_url}{path}",
headers=self.get_headers()
)
response.raise_for_status()
return response.json()
# Usage
client = AuthenticatedClient(
base_url="http://localhost:8080",
email="user@example.com",
password="secure-password"
)
datasets = client.get("/api/v1/datasets/")
JavaScript with automatic retry
class AuthenticatedClient {
constructor(baseUrl, email, password) {
this.baseUrl = baseUrl
this.email = email
this.password = password
this.token = null
}
async login() {
const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: this.email,
password: this.password
})
})
if (!response.ok) throw new Error('Login failed')
const data = await response.json()
this.token = data.access_token
}
async request(path, options = {}) {
if (!this.token) await this.login()
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
}
})
// Retry once on 401
if (response.status === 401) {
await this.login()
return fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
}
})
}
return response
}
}
// Usage
const client = new AuthenticatedClient(
'http://localhost:8080',
'user@example.com',
'secure-password'
)
const response = await client.request('/api/v1/datasets/')
const datasets = await response.json()