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.