Skip to main content
This guide provides example REST API endpoints for dashboard operations in your backend.

Overview

Your API should expose endpoints for:
  • Creating dashboards for tenants
  • Retrieving tenant dashboards
  • Generating iframe embed tokens
  • Managing dashboard access

Authentication Middleware

All endpoints should require authentication. Here’s an example middleware:
from fastapi import Depends, HTTPException, Header
from jose import jwt, JWTError

async def get_authenticated_user(
    authorization: str = Header(None)
) -> dict:
    """Verify JWT token and return user info"""
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Missing authorization")

    token = authorization.replace("Bearer ", "")

    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")

Create Dashboard Endpoint

The create_dashboard function being called in the endpoint below should have already been created in the Creating Dashboards guide.
Create a new dashboard for a tenant.
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel

router = APIRouter(prefix="/api/v1/dashboards", tags=["dashboards"])

class DashboardCreate(BaseModel):
    tenant_id: str
    workspace_id: str
    name: str
    description: Optional[str] = None

@router.post("/")
async def create_dashboard(
    dashboard: DashboardCreate,
    current_user: Dict = Depends(get_authenticated_user),
    db: Session = Depends(get_db)
):
    """Create a dashboard for a tenant"""
    # Verify user has permission to create dashboard for this tenant
    if not has_access(current_user, dashboard.tenant_id):
        raise HTTPException(status_code=403, detail="Access denied")

    handler = DashboardHandler(db)
    result = await handler.create_dashboard(
        tenant_id=dashboard.tenant_id,
        workspace_id=dashboard.workspace_id,
        name=dashboard.name,
        description=dashboard.description,
        created_by=current_user["user_id"]
    )

    return {
        "success": True,
        "data": result
    }
Request:
POST /api/v1/dashboards
Authorization: Bearer YOUR_JWT_TOKEN

{
  "tenant_id": "org-123",
  "workspace_id": "workspace-456",
  "name": "Sales Dashboard",
  "description": "Q4 Sales Analytics"
}
Response:
{
  "success": true,
  "data": {
    "id": "dashboard-789",
    "tenant_id": "org-123",
    "workspace_id": "workspace-456",
    "dashboard_id": "dashboard-789",
    "created_at": "2024-01-15T10:30:00Z"
  }
}

Get Dashboard by Tenant

Retrieve the dashboard for a specific tenant.
@router.get("/{tenant_id}")
def get_dashboard_by_tenant(
    tenant_id: str,
    current_user: Dict = Depends(get_authenticated_user),
    db: Session = Depends(get_db)
):
    """Get dashboard for a specific tenant"""
    # Verify access
    if not has_access(current_user, tenant_id):
        raise HTTPException(status_code=403, detail="Access denied")

    handler = DashboardHandler(db)
    dashboard = handler.get_dashboard_by_tenant(tenant_id)

    if not dashboard:
        raise HTTPException(status_code=404, detail="Dashboard not found")

    return {
        "success": True,
        "data": dashboard
    }
Request:
GET /api/v1/dashboards/org-123
Authorization: Bearer YOUR_JWT_TOKEN
Response:
{
  "success": true,
  "data": {
    "id": "dashboard-789",
    "tenant_id": "org-123",
    "workspace_id": "workspace-456",
    "dashboard_id": "dashboard-789",
    "created_at": "2024-01-15T10:30:00Z"
  }
}

Generate Iframe Token

Generate a secure token for embedding a dashboard.
class DashboardIframeTokenRequest(BaseModel):
    tenant_id: str
    workspace_id: str
    dashboard_id: str
    valid_until: Optional[int] = None

@router.post("/iframe-token")
def create_iframe_token(
    request: DashboardIframeTokenRequest,
    current_user: Dict = Depends(get_authenticated_user),
    db: Session = Depends(get_db)
):
    """Generate secure token for embedding dashboard in iframe"""
    # Verify tenant belongs to user
    if not has_access(current_user, request.tenant_id):
        raise HTTPException(status_code=403, detail="Access denied")

    handler = DashboardHandler(db)
    token_data = handler.generate_iframe_token(
        tenant_id=request.tenant_id,
        workspace_id=request.workspace_id,
        dashboard_id=request.dashboard_id,
        valid_until=request.valid_until
    )

    return {
        "success": True,
        "data": token_data
    }
Request:
POST /api/v1/dashboards/iframe-token
Authorization: Bearer YOUR_JWT_TOKEN

{
  "tenant_id": "org-123",
  "workspace_id": "workspace-456",
  "dashboard_id": "dashboard-789",
  "valid_until": 1700000000  // Optional: Unix timestamp
}
Response:
{
  "success": true,
  "data": {
    "token": "eyJhcGlfa2V5X2lkIjoieW91ci1hcGkta2V5IiwidGVuYW50X2lkIjoib3JnLTEyMyIsImRhc2hib2FyZF9pZCI6ImRhc2hib2FyZC03ODkiLCJ2YWxpZF91bnRpbCI6MTcwMDAwMDAwMCwic2lnbmF0dXJlIjoiYWJjMTIzLi4uIn0=",
    "dashboard_id": "dashboard-789",
    "expires_at": 1700000000
  }
}

List All Dashboards (Optional)

List all dashboards for the authenticated user’s organization.
@router.get("/")
def list_dashboards(
    current_user: Dict = Depends(get_authenticated_user),
    db: Session = Depends(get_db),
    skip: int = 0,
    limit: int = 100
):
    """List all dashboards for user's organization"""
    organization_id = current_user.get("organization_id")

    handler = DashboardHandler(db)
    dashboards = handler.list_dashboards(
        organization_id=organization_id,
        skip=skip,
        limit=limit
    )

    return {
        "success": True,
        "data": dashboards,
        "total": len(dashboards)
    }

Delete Dashboard (Optional)

Delete a dashboard for a tenant.
@router.delete("/{tenant_id}")
async def delete_dashboard(
    tenant_id: str,
    current_user: Dict = Depends(get_authenticated_user),
    db: Session = Depends(get_db)
):
    """Delete dashboard for a tenant"""
    # Verify access
    if not has_access(current_user, tenant_id):
        raise HTTPException(status_code=403, detail="Access denied")

    handler = DashboardHandler(db)
    success = await handler.delete_dashboard(tenant_id)

    if not success:
        raise HTTPException(status_code=404, detail="Dashboard not found")

    return {
        "success": True,
        "message": "Dashboard deleted successfully"
    }

Error Handling

Implement consistent error handling across all endpoints:
// Success Response
{
  "success": true,
  "data": { ... }
}

// Error Response
{
  "success": false,
  "error": "Error message here",
  "code": "ERROR_CODE"
}

Common Error Codes

CodeStatusDescription
UNAUTHORIZED401Missing or invalid authentication
FORBIDDEN403User doesn’t have access to resource
NOT_FOUND404Resource not found
VALIDATION_ERROR422Invalid request parameters
SERVER_ERROR500Internal server error

Rate Limiting

Implement rate limiting to prevent abuse:
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@router.post("/iframe-token")
@limiter.limit("10/minute")
async def create_iframe_token(...):
    # ... implementation

Next Steps