Skip to content

Events Dashboard — Frontend Integration Guide

Endpoints for the admin events panel. Provides event KPIs, per-event stats, promo code stats, ticket order stats, and XLSX export.

Prefix: /api/v1/admin/events-dashboard Authentication: Bearer token (JWT) + user_type='admin'


Endpoints

Method Endpoint Description
GET /admin/events-dashboard/overview Consolidated event KPIs
GET /admin/events-dashboard/events Per-event stats (paginated)
GET /admin/events-dashboard/promo-codes Promo code stats
GET /admin/events-dashboard/orders Ticket order stats
GET /admin/events-dashboard/events/export Download XLSX report

1. GET /admin/events-dashboard/overview

Consolidated KPIs for events, tickets, and revenue. Ready for dashboard cards.

GET /api/v1/admin/events-dashboard/overview
Authorization: Bearer <admin_token>

Response (200 OK)

{
  "total_events": 12,
  "active_events": 5,
  "upcoming_events": 3,
  "past_events": 9,
  "total_tickets_issued": 347,
  "total_tickets_sold": 210,
  "total_tickets_promo": 137,
  "total_revenue": 10500.00,
  "total_orders": 98,
  "events_by_type": {
    "movie_premiere": 8,
    "concert": 4
  }
}

Fields

Field Type Description
total_events int Total registered events
active_events int Events with is_active=true
upcoming_events int Events with event_date > now
past_events int Events with event_date <= now
total_tickets_issued int All tickets in the system
total_tickets_sold int Tickets created via store purchase (ticket_order_id present)
total_tickets_promo int Tickets created via promo code (promo_code_id present, no ticket_order_id)
total_revenue float Revenue from confirmed orders (reais, e.g. 10500.00 = R$10.500,00)
total_orders int Total confirmed ticket orders
events_by_type dict[str, int] Count per event type key

Flutter/Dart

class EventsOverview {
  final int totalEvents;
  final int activeEvents;
  final int upcomingEvents;
  final int pastEvents;
  final int totalTicketsIssued;
  final int totalTicketsSold;
  final int totalTicketsPromo;
  final double totalRevenue;
  final int totalOrders;
  final Map<String, int> eventsByType;

  EventsOverview.fromJson(Map<String, dynamic> json)
      : totalEvents = json['total_events'],
        activeEvents = json['active_events'],
        upcomingEvents = json['upcoming_events'],
        pastEvents = json['past_events'],
        totalTicketsIssued = json['total_tickets_issued'],
        totalTicketsSold = json['total_tickets_sold'],
        totalTicketsPromo = json['total_tickets_promo'],
        totalRevenue = (json['total_revenue'] as num).toDouble(),
        totalOrders = json['total_orders'],
        eventsByType = Map<String, int>.from(json['events_by_type'] ?? {});
}

Future<EventsOverview> fetchOverview(String token) async {
  final response = await dio.get(
    '/api/v1/admin/events-dashboard/overview',
    options: Options(headers: {'Authorization': 'Bearer $token'}),
  );
  return EventsOverview.fromJson(response.data);
}

2. GET /admin/events-dashboard/events

Per-event stats with ticket and order breakdowns. Paginated.

GET /api/v1/admin/events-dashboard/events?page=0&limit=20
Authorization: Bearer <admin_token>

Query Params

Param Type Default Description
page int 0 Page offset (0-based)
limit int 20 Items per page (max 500)
event_type string Filter by event type (e.g. movie_premiere)
active_only bool false If true, only events with is_active=true

Response (200 OK)

{
  "total": 12,
  "events": [
    {
      "event_id": "665a1b2c3d4e5f6a7b8c9d0e",
      "title": "Pre-Estreia: O Retorno",
      "event_type": "movie_premiere",
      "event_date": "2026-04-15T19:00:00+00:00",
      "location": "Cinema Central - Sala 3",
      "capacity": 200,
      "tickets_issued": 147,
      "tickets_sold": 110,
      "tickets_promo": 37,
      "ticket_status": {
        "available": 100,
        "consumed": 40,
        "expired": 7
      },
      "revenue": 5500.00,
      "orders_count": 55,
      "occupancy_pct": 73.5
    }
  ]
}

Fields — event item

Field Type Description
event_id string MongoDB ObjectId
title string Event title
event_type string Event type key (e.g. movie_premiere)
event_date string \| null ISO 8601 datetime
location string Venue
capacity int Max tickets (0 = unlimited/not set)
tickets_issued int Real ticket count from tickets collection (via $lookup)
tickets_sold int Tickets created via store purchase (ticket_order_id present)
tickets_promo int Tickets created via promo code (promo_code_id present, no ticket_order_id)
ticket_status.available int Tickets with status='available'
ticket_status.consumed int Tickets with status='consumed'
ticket_status.expired int Tickets with status='expired'
revenue float Sum of paid orders (reais)
orders_count int Count of paid orders
occupancy_pct float tickets_issued / capacity * 100 (0.0 if no capacity)

Note: tickets_issued here is the live count from a $lookup join, not the denormalized tickets_issued counter on the event document. These should match but the dashboard always shows the accurate value.

Flutter/Dart

Future<Map<String, dynamic>> fetchEventStats({
  int page = 0,
  int limit = 20,
  String? eventType,
  bool activeOnly = false,
}) async {
  final response = await dio.get(
    '/api/v1/admin/events-dashboard/events',
    queryParameters: {
      'page': page,
      'limit': limit,
      if (eventType != null) 'event_type': eventType,
      if (activeOnly) 'active_only': true,
    },
    options: Options(headers: {'Authorization': 'Bearer $token'}),
  );
  return response.data; // { total, events: [...] }
}

3. GET /admin/events-dashboard/promo-codes

Promo code stats for events.

GET /api/v1/admin/events-dashboard/promo-codes
Authorization: Bearer <admin_token>

Response (200 OK)

{
  "total_promo_codes": 25,
  "active_promo_codes": 18,
  "total_tickets_from_promos": 210,
  "by_type": {
    "ticket": {
      "total": 20,
      "active": 15,
      "tickets_generated": 190
    },
    "promo": {
      "total": 5,
      "active": 3,
      "tickets_generated": 0
    }
  },
  "top_codes": [
    {
      "code": "449GQW",
      "code_type": "ticket",
      "title": "Goiania VIP",
      "tickets_generated": 45,
      "is_active": true
    }
  ]
}

Fields

Field Type Description
total_promo_codes int Total promo codes in the system
active_promo_codes int Codes with is_active=true
total_tickets_from_promos int All tickets created via promo codes (promo_code_id present, no ticket_order_id)
by_type dict[str, object] Breakdown by code_type key
by_type[key].total int Total codes of this type
by_type[key].active int Active codes of this type
by_type[key].tickets_generated int Tickets issued by this type
top_codes list Top 10 codes by tickets generated
top_codes[].code string Promo code string
top_codes[].code_type string ticket or promo
top_codes[].title string Promo code title
top_codes[].tickets_generated int Tickets issued by this code
top_codes[].is_active bool Whether code is currently active

4. GET /admin/events-dashboard/orders

Ticket order stats.

GET /api/v1/admin/events-dashboard/orders
Authorization: Bearer <admin_token>

Response (200 OK)

{
  "total_orders": 156,
  "total_revenue": 17350.00,
  "by_status": {
    "CONFIRMED": { "count": 110, "total_amount": 11000.00 },
    "RECEIVED": { "count": 20, "total_amount": 2000.00 },
    "PENDING": { "count": 8, "total_amount": 800.00 },
    "succeeded": { "count": 18, "total_amount": 3550.00 }
  },
  "by_gateway": {
    "asaas": { "count": 98, "total_amount": 9800.00 },
    "stripe": { "count": 58, "total_amount": 7550.00 }
  },
  "by_payment_method": {
    "credit_card": { "count": 89, "total_amount": 8900.00 },
    "pix": { "count": 67, "total_amount": 8450.00 }
  },
  "avg_order_value": 111.22,
  "avg_tickets_per_order": 1.5
}

Fields

Field Type Description
total_orders int Total ticket orders (all statuses)
total_revenue float Sum of all order amounts (reais)
by_status dict[str, object] Breakdown by gateway payment status (Asaas: CONFIRMED, RECEIVED, PENDING, OVERDUE; Stripe: succeeded, requires_action, canceled)
by_gateway dict[str, object] Breakdown by payment gateway (asaas, stripe)
by_payment_method dict[str, object] Breakdown by payment method (credit_card, pix, unknown for legacy orders)
avg_order_value float Average order value in reais
avg_tickets_per_order float Average tickets per order

Each breakdown object (by_status, by_gateway, by_payment_method) has:

Sub-field Type Description
count int Number of orders in this group
total_amount float Total amount for this group (reais)

Flutter/Dart

class OrderStats {
  final int totalOrders;
  final double totalRevenue;
  final Map<String, Map<String, dynamic>> byStatus;
  final Map<String, Map<String, dynamic>> byGateway;
  final Map<String, Map<String, dynamic>> byPaymentMethod;
  final double avgOrderValue;
  final double avgTicketsPerOrder;

  OrderStats.fromJson(Map<String, dynamic> json)
      : totalOrders = json['total_orders'],
        totalRevenue = (json['total_revenue'] as num).toDouble(),
        byStatus = Map<String, Map<String, dynamic>>.from(
            (json['by_status'] as Map).map(
                (k, v) => MapEntry(k as String, Map<String, dynamic>.from(v)))),
        byGateway = Map<String, Map<String, dynamic>>.from(
            (json['by_gateway'] as Map).map(
                (k, v) => MapEntry(k as String, Map<String, dynamic>.from(v)))),
        byPaymentMethod = Map<String, Map<String, dynamic>>.from(
            (json['by_payment_method'] as Map).map(
                (k, v) => MapEntry(k as String, Map<String, dynamic>.from(v)))),
        avgOrderValue = (json['avg_order_value'] as num).toDouble(),
        avgTicketsPerOrder = (json['avg_tickets_per_order'] as num).toDouble();
}

5. GET /admin/events-dashboard/events/export

Download all events with ticket and order stats as xlsx (default), csv (zip multi-aba) or json. Style follows ADR-059.

GET /api/v1/admin/events-dashboard/events/export?format=xlsx
Authorization: Bearer <admin_token>

Query Params

Param Type Default Description
format string xlsx xlsx (multi-sheet) / csv (zip) / json (nested)
event_type string Filter by event type
active_only bool false Only active events
token string Bearer token (alternative to Authorization header — use for direct URL downloads)

Response

format Content-Type Filename
xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet events_dashboard_YYYYMMDD_HHMM.xlsx
csv application/zip events_dashboard_YYYYMMDD_HHMM.zip
json application/json; charset=utf-8 events_dashboard_YYYYMMDD_HHMM.json

The file contains two sheets:

Sheet 1 — Events

Column Description
ID Event ObjectId
Title Event title
Type event_type key
Date DD/MM/YYYY HH:MM
Location Venue
Capacity Max tickets
Tickets Issued Live count
Available status='available' count
Consumed status='consumed' count
Expired status='expired' count
Tickets Sold Tickets from purchases
Tickets Promo Tickets from promo codes
Revenue (R$) Paid order revenue
Orders (paid) Count of paid orders
Occupancy % issued / capacity * 100
Active Yes / No
Box Office Yes / No (is_sale_box_office)

Sheet 2 — Tickets

Column Description
Ticket ID Ticket ObjectId
Event Event title
Event Date DD/MM/YYYY HH:MM
Customer ID Customer ObjectId
Status available / consumed / expired
Promo Code Code string (if from promo)
Order ID TicketOrder ObjectId (if from purchase)
Created At DD/MM/YYYY HH:MM
Consumed At DD/MM/YYYY HH:MM (if consumed)

Flutter/Dart — Download and save

Dois modos suportados:

Modo 1 — Dio com header (recomendado):

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

Future<File> downloadEventsDashboardXlsx({
  required String token,
  String? eventType,
  bool activeOnly = false,
}) async {
  final queryParams = <String, dynamic>{};
  if (eventType != null) queryParams['event_type'] = eventType;
  if (activeOnly) queryParams['active_only'] = true;

  final response = await Dio().get(
    '$baseUrl/api/v1/admin/events-dashboard/events/export',
    queryParameters: queryParams,
    options: Options(
      headers: {'Authorization': 'Bearer $token'},
      responseType: ResponseType.bytes,
    ),
  );

  final dir = await getApplicationDocumentsDirectory();
  final filename = 'events_dashboard_${DateTime.now().millisecondsSinceEpoch}.xlsx';
  final file = File('${dir.path}/$filename');
  await file.writeAsBytes(response.data as List<int>);
  return file;
}

Modo 2 — URL direta com token como query param (para launchUrl, WebView, share link):

import 'package:url_launcher/url_launcher.dart';

Future<void> openXlsxInBrowser(String token) async {
  final uri = Uri.parse(
    '$baseUrl/api/v1/admin/events-dashboard/events/export?token=$token',
  );
  await launchUrl(uri, mode: LaunchMode.externalApplication);
}

Notes

  • Revenue in reais (not cents). e.g. 17350.00 = R$17.350,00.
  • Admin authentication required on all endpoints.
  • Ticket origin discrimination: Tickets are classified by existing fields — ticket_order_id (store purchase) and promo_code_id (promo code). Tickets with neither are admin-created.
  • Order status: by_status shows gateway-native status values (CONFIRMED/RECEIVED for Asaas, succeeded for Stripe), not normalized lifecycle states.
  • Confirmed payment statuses: CONFIRMED, RECEIVED (Asaas), succeeded (Stripe) — used for revenue and paid order counts.
  • Aggregation pipelines with $lookup — may have higher latency on large datasets.
  • events/export is route-order sensitive — registered before events/{event_id} in the router, so it is matched correctly.