Impersonation (Cross-Tenant Access)
Resellers can access their managed teams' data in three ways:
- X-Tenant-Id header — Use your own token with a tenant context header (simple, stateless)
- Impersonation tokens — Create short-lived tokens scoped to the target tenant (for automation, no header needed)
- SSO login links — Generate one-click login URLs that authenticate users into the dashboard (for end-user access, no password needed)
Method 1: X-Tenant-Id Header
Resellers can access their managed teams' data by setting the X-Tenant-Id header on any API request. This switches the database context so all queries are scoped to the target tenant.
How It Works
- You authenticate with your own credentials and receive a Bearer token
- You add
X-Tenant-Id: {managed-tenant-uuid}to your API requests - The API validates that you manage the target tenant
- Data isolation is enforced at the infrastructure level for the target tenant
- All data returned (and created) belongs to the target tenant
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Reseller │────>│ Tenant Context │────>│ Isolated │
│ Request │ │ Middleware │ │ Data │
│ │ │ │ │ │
│ Auth: Bearer │ │ 1. Verify token │ │ Scoped to │
│ X-Tenant-Id │ │ 2. Check owner │ │ target team │
│ │ │ 3. Set context │ │ │
└─────────────┘ └──────────────────┘ └─────────────┘Who Can Impersonate
| Role | Behavior |
|---|---|
| Super admin | Can set X-Tenant-Id to any tenant |
| Reseller | Can only set X-Tenant-Id to tenants in their managed_tenants list |
| Admin / Member | X-Tenant-Id header is silently ignored; requests always use own tenant |
Request Examples
List Monitors in a Managed Team
curl https://app.hoppla.dev/api/v1/monitors \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: 550e8400-e29b-41d4-a716-446655440000"Create a Monitor in a Managed Team
curl -X POST https://app.hoppla.dev/api/v1/monitors \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Website",
"url": "https://customer.com",
"type": "uptime",
"interval": 60,
"is_enabled": true
}'View Incidents in a Managed Team
curl https://app.hoppla.dev/api/v1/incidents \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: 550e8400-e29b-41d4-a716-446655440000"Finding the Tenant ID
The tenant_id for each managed team is returned in several places:
POST /auth/tokenresponse (in themanaged_teamsarray)GET /reseller/managed-teamsresponse (thetenant_idfield on each team)POST /reseller/managed-teamsresponse (when creating a new team)
Dashboard: "View as This Team"
In the dashboard, impersonation works via the team selector:
- Go to Teams in the sidebar
- Click View as this team on any managed team
- The dashboard switches context — all pages now show the team's data
- Click the team selector dropdown and choose Back to my account to return
Security Model
- Ownership verified on every request: Every API call verifies that you manage the target tenant. There is no caching — the check is performed live.
- Infrastructure-level isolation: Data isolation is enforced at the infrastructure level, not just in application code. Even in the event of application bugs, cross-tenant data access is prevented.
- Audit trail: All actions performed while impersonating are attributed to the reseller's user ID, not the managed team's users.
- No privilege escalation: Impersonation does not grant super admin access. You can perform standard API operations (CRUD monitors, view incidents, etc.) but cannot modify tenant settings or user roles within the managed team.
Error Responses
| Status | Scenario |
|---|---|
| 403 | X-Tenant-Id points to a tenant you do not manage |
| 403 | X-Tenant-Id is a valid UUID but the tenant does not exist |
| 400 | X-Tenant-Id is not a valid UUID format |
Example error:
{
"message": "You do not manage this tenant."
}Caveats
- Notification rules and channels are not shared across tenants. If you create a notification channel while impersonating, it belongs to the managed team.
- Reseller webhook events always go to the reseller's notification channels, not the managed team's.
- Rate limits are counted per token, not per tenant context. Impersonating multiple teams does not give you additional rate limit quota.
- Branding is per-tenant. Use
GET/PATCH /reseller/brandingwithX-Tenant-Idto manage a managed team's branding. SMTP config (/smtp-config) is similarly scoped to the impersonated tenant.
Method 2: Impersonation Tokens
For automated workflows (CI/CD, provisioning scripts), you can create short-lived tokens that are pre-scoped to a managed team's tenant. These tokens work as standard Bearer tokens without needing the X-Tenant-Id header.
How It Works
- You call
POST /reseller/managed-teams/{id}/impersonation-tokenswith a TTL (1–15 minutes; default and maximum 15) - The API creates a service user in the target tenant (if one doesn't already exist)
- A token is created for that service user with
read+writescopes - You use the returned
plain_text_tokenas a regular Bearer token - The token automatically operates in the managed team's tenant context
- The token expires after the specified TTL
┌─────────────┐ ┌────────────────┐ ┌─────────────┐
│ Reseller │────>│ Create │────>│ Service │
│ Account │ │ Impersonation│ │ User Token │
│ │ │ Token │ │ (in target │
│ POST /reseller│ │ │ │ tenant) │
│ /managed-teams│ │ 1. Find team │ │ │
│ /{id}/imperf- │ │ 2. Create svc │ │ Expires in │
│ sonation-tokens│ │ user │ │ N minutes │
└──────────────┘ │ 3. Issue token │ └──────────────┘
│ 4. Audit log │
└────────────────┘Create an Impersonation Token
curl -X POST https://app.hoppla.dev/api/v1/reseller/managed-teams/mt-uuid-.../impersonation-tokens \
-H "Authorization: Bearer $RESELLER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ttl_minutes": 10}'Response:
{
"data": {
"id": "token-uuid-...",
"managed_team_id": "mt-uuid-...",
"managed_tenant_id": "tenant-uuid-...",
"scopes": ["read", "write"],
"expires_at": "2026-03-10T14:10:00.000000Z",
"ttl_minutes": 10
},
"plain_text_token": "ik-9xKsecretImpersonationToken..."
}Use the Impersonation Token
No X-Tenant-Id header needed — the token is already scoped:
# List monitors in the managed team
curl https://app.hoppla.dev/api/v1/monitors \
-H "Authorization: Bearer ik-9xKsecretImpersonationToken..."
# Create a monitor in the managed team
curl -X POST https://app.hoppla.dev/api/v1/monitors \
-H "Authorization: Bearer ik-9xKsecretImpersonationToken..." \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Website",
"url": "https://customer.com",
"type": "uptime",
"interval": 60,
"is_enabled": true
}'List Active Tokens
curl https://app.hoppla.dev/api/v1/reseller/managed-teams/mt-uuid-.../impersonation-tokens \
-H "Authorization: Bearer $RESELLER_TOKEN"Revoke a Token Early
curl -X DELETE https://app.hoppla.dev/api/v1/reseller/managed-teams/mt-uuid-.../impersonation-tokens/token-uuid-... \
-H "Authorization: Bearer $RESELLER_TOKEN"Limits and Security
- TTL: 1–15 minutes (default and maximum 15) — tokens cannot be extended, only re-created. They cannot be made persistent.
- Max active tokens: 5 per managed team at any time
- Audit trail: Every impersonation token creation is logged on both the reseller's and the managed team's audit trail, with the reseller's user ID and the target team
- Auto-expiry: Expired tokens are automatically rejected
- Service user: A dedicated service user is created in the target tenant for token ownership
Method 3: SSO Login Links
Generate one-click login URLs that authenticate managed team users directly into the Hoppla dashboard. This is the simplest way to provide single sign-on for your customers.
How It Works
- You call
POST /reseller/managed-teams/{id}/sso-linkwith the target user's email - The API generates a signed JWT (HS256) and returns a full login URL
- The user clicks the link and is authenticated into the dashboard — no password needed
- The link expires after 5 minutes and can only be used once
┌─────────────┐ ┌────────────────┐ ┌─────────────┐
│ Reseller │────>│ Generate │────>│ SSO URL │
│ Platform │ │ SSO Link │ │ │
│ │ │ │ │ /sso/partner │
│ POST /reseller│ │ 1. Auto-config │ │ ?token=JWT │
│ /managed-teams│ │ 2. Sign JWT │ │ &tenant=slug │
│ /{id}/sso-link│ │ 3. Return URL │ │ │
└──────────────┘ └────────────────┘ │ User clicks → │
│ Authenticated │
└──────────────┘Generate a Login Link
curl -X POST https://app.hoppla.dev/api/v1/reseller/managed-teams/mt-uuid-.../sso-link \
-H "Authorization: Bearer $RESELLER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_email": "[email protected]", "user_name": "Alice"}'Response:
{
"data": {
"sso_url": "https://monitoring.acme.com/sso/partner?token=eyJ...&tenant=acme-corp",
"expires_in_seconds": 300,
"user_email": "[email protected]",
"user_exists": true
}
}Share the sso_url with your user, embed it in your dashboard, or redirect to it.
Portal-aware URLs: The generated URL automatically uses the best available portal domain (managed team's domain > reseller's domain > app.hoppla.dev). This keeps the session cookie on the correct domain so users stay authenticated.
Auto-provisioning
You do not need to manually configure Partner SSO before using login links. The endpoint automatically:
- Generates a shared secret (if none exists)
- Enables Partner SSO on the managed team
- Enables auto-registration (new users are created on first login)
- Enables account linking (existing password users are linked to SSO)
Auto-registration only creates people who don't have an account yet. If user_email already belongs to an account elsewhere, the login is refused. Invite that person to the team first; after they accept, login links work for them.
A session opened through a login link is limited to that team. It cannot switch to the person's other teams, and it cannot change their password, two-factor settings or passkeys. For those they sign in with their own password or passkey.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
user_email | yes | Email of the user to log in | |
user_name | string | no | Display name (used if auto-registering a new user) |
redirect_to | string | no | Dashboard path to redirect to after login, e.g. /dashboard/monitors |
Security
- Links expire after 5 minutes
- Each link can only be used once (JWT replay prevention via
jticlaim) - All generated links are logged to the managed team's audit trail
- The underlying Partner SSO mechanism uses HS256 JWT signing
- When
hide_password_loginis enabled, password-based login is blocked server-side (not just hidden in the UI) — API calls to/auth/loginand/auth/tokenare rejected for users in that tenant
When to Use Which Method
| Scenario | Recommended Method |
|---|---|
| Interactive dashboard browsing | X-Tenant-Id header |
| Quick one-off API calls | X-Tenant-Id header |
| CI/CD pipeline automation | Impersonation tokens |
| Provisioning scripts | Impersonation tokens |
| Delegating access to a service | Impersonation tokens |
| Long-running operations (>15 min) | X-Tenant-Id header |
| End-user login from your platform | SSO login links |
| Customer self-service dashboard access | SSO login links |
| Embedding Hoppla in your product | SSO login links + hide password login |