Partner API Documentation
Overview
Partner organizations can generate magic links programmatically using their organization secret key. This allows partners to integrate Tempo signup into their own systems.
Authentication
Partners authenticate using their magicLinkSecret
which is provided when the organization is created. This secret should be kept secure and not exposed in client-side code.
Generate Magic Link
Endpoint
POST /api/partner/generate-link
Request Body
{
"orgSecret": "your-organization-secret-here",
"email": "user@example.com",
"name": "John Doe",
"expiresInDays": 7,
"metadata": {
"source": "partner-website",
"campaign": "spring-2024"
}
}
Parameters
orgSecret
(required): Your organization's secret keyemail
(required): The email address for the new username
(optional): The user's name (defaults to email prefix)expiresInDays
(optional): Link expiration in days (1-30, defaults to 7)metadata
(optional): Additional tracking data
Response
{
"success": true,
"magicLink": "https://app.ontempo.io/api/auth/partner-callback?token=...",
"expiresAt": "2024-01-15T10:30:00.000Z",
"organization": {
"name": "Your Partner Org",
"discountPercent": 20,
"welcomeMessage": "Welcome to Tempo via Your Partner Org!"
}
}
Validate Organization
Endpoint
GET /api/partner/generate-link?orgSecret=your-secret
Response
{
"success": true,
"organization": {
"id": "org-id",
"name": "Your Partner Org",
"slug": "your-partner-org",
"description": "Partner organization description",
"discountPercent": 20,
"revenueSharePercent": 15,
"isActive": true,
"trackingCode": "PARTNER-001",
"welcomeMessage": "Welcome message",
"_count": {
"users": 42
}
}
}
Example Usage
Node.js
const response = await fetch('https://app.ontempo.io/api/partner/generate-link', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
orgSecret: 'your-organization-secret',
email: 'customer@example.com',
name: 'Customer Name',
expiresInDays: 7,
metadata: {
source: 'partner-portal',
referralCode: 'SPRING2024'
}
})
});
const data = await response.json();
if (data.success) {
console.log('Magic link generated:', data.magicLink);
// Send the magic link to the customer via email
} else {
console.error('Error:', data.error);
}
Python
import requests
response = requests.post('https://app.ontempo.io/api/partner/generate-link', json={
'orgSecret': 'your-organization-secret',
'email': 'customer@example.com',
'name': 'Customer Name',
'expiresInDays': 7,
'metadata': {
'source': 'partner-portal',
'referralCode': 'SPRING2024'
}
})
data = response.json()
if data.get('success'):
print(f"Magic link generated: {data['magicLink']}")
# Send the magic link to the customer via email
else:
print(f"Error: {data.get('error')}")
Error Responses
400 Bad Request
- Missing required fields
- Invalid email format
- Invalid expiresInDays value
401 Unauthorized
- Invalid organization secret
403 Forbidden
- Organization is not active
500 Internal Server Error
- Server error
Security Notes
- Keep your
orgSecret
secure - Never expose it in client-side code - Use HTTPS - Always make requests over HTTPS
- Validate emails - The API validates email format, but additional validation is recommended
- Rate limiting - Consider implementing rate limiting on your end to prevent abuse
- Link expiration - Magic links expire based on the
expiresInDays
parameter
Recommended Integration Pattern
On-Demand Link Generation (Recommended)
For the best user experience, generate magic links on-demand when users click signup buttons in your dashboard. This eliminates expiry issues and ensures fresh links every time.
Architecture:
Partner Dashboard → Partner API → Tempo API → Fresh Magic Link → User Redirect
Implementation:
Partner Frontend (React/Vue/etc):
const handleSignupClick = async () => {
try {
const response = await fetch('/partner/api/generate-tempo-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userEmail: currentUser.email,
userName: currentUser.name,
source: 'dashboard-signup-button',
userId: currentUser.id
})
});
const data = await response.json();
if (data.success) {
// Immediate redirect to Tempo
window.location.href = data.magicLink;
} else {
throw new Error(data.error);
}
} catch (error) {
alert('Signup temporarily unavailable. Please try again.');
}
};
<button onClick={handleSignupClick}>
Sign up for Tempo
</button>
Partner Backend (Node.js/Express):
app.post('/partner/api/generate-tempo-link', async (req, res) => {
try {
const { userEmail, userName, source, userId } = req.body;
const response = await fetch('https://app.ontempo.io/api/partner/generate-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orgSecret: process.env.TEMPO_ORG_SECRET,
email: userEmail,
name: userName,
expiresInDays: 1, // Short expiry since immediate use
metadata: {
source,
partner_user_id: userId,
signup_context: 'dashboard',
timestamp: new Date().toISOString()
}
})
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: 'Failed to generate signup link' });
}
});
Partner Backend (Python/FastAPI):
@app.post("/partner/api/generate-tempo-link")
async def generate_tempo_link(request_data: dict):
try:
response = requests.post(
'https://app.ontempo.io/api/partner/generate-link',
json={
'orgSecret': os.environ['TEMPO_ORG_SECRET'],
'email': request_data['userEmail'],
'name': request_data['userName'],
'expiresInDays': 1, # Short expiry since immediate use
'metadata': {
'source': request_data['source'],
'partner_user_id': request_data['userId'],
'signup_context': 'dashboard',
'timestamp': datetime.now().isoformat()
}
}
)
return response.json()
except Exception as e:
return {"error": "Failed to generate signup link"}
Benefits of This Pattern:
- No expiry issues - Links generated fresh on each click
- Secure -
orgSecret
never exposed to client-side - Current user data - Always uses up-to-date user information
- Rich tracking - Include current session/campaign data
- Error handling - Graceful fallback if API is unavailable
- User experience - Seamless transition from partner to Tempo
Alternative: Email Delivery
For email-based workflows, you can also send magic links via email:
// Generate link and send via email instead of redirect
const data = await response.json();
await sendEmail({
to: userEmail,
subject: 'Your Tempo Account is Ready!',
body: `Click here to access your account: ${data.magicLink}`
});
Integration Tips
- On-demand generation - Generate links when users click, not in advance
- Short expiry - Use 1-day expiry for immediate use links
- Tracking - Use the
metadata
field to track conversions and campaigns - Error handling - Always handle API errors gracefully
- Testing - Test with the development environment first
- Monitoring - Monitor your organization's user count and conversion rates
- User feedback - Show loading states during link generation
- Fallback - Have a backup plan if the API is temporarily unavailable
Support
For technical support or to request a partner organization, contact the Tempo team at support@ontempo.io.