Skip to main content

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.

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 key
  • email (required): The email address for the new user
  • name (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

  1. Keep your orgSecret secure - Never expose it in client-side code
  2. Use HTTPS - Always make requests over HTTPS
  3. Validate emails - The API validates email format, but additional validation is recommended
  4. Rate limiting - Consider implementing rate limiting on your end to prevent abuse
  5. Link expiration - Magic links expire based on the expiresInDays parameter

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

  1. On-demand generation - Generate links when users click, not in advance
  2. Short expiry - Use 1-day expiry for immediate use links
  3. Tracking - Use the metadata field to track conversions and campaigns
  4. Error handling - Always handle API errors gracefully
  5. Testing - Test with the development environment first
  6. Monitoring - Monitor your organization's user count and conversion rates
  7. User feedback - Show loading states during link generation
  8. 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.