Levelbrook Labs

Building a Media Rights & Royalties Dashboard

Try the interactive demo

The business of media isn't just content creation; it's the management of a complex web of intellectual property rights. A single piece of content—a song, a film, a TV series—is not a monolithic asset. It's a bundle of rights that can be sliced and diced across numerous dimensions: territory, time, language, distribution platform, and exclusivity. Building software to manage this is a fascinating engineering problem, sitting at the intersection of complex data modeling, demanding UX, and high-stakes financial calculation.

The Domain Problem: A Combinatorial Explosion

At its core, content rights management is about answering one question, repeatedly and with perfect accuracy: "Can we use this content, for this purpose, in this place, at this time?" This is deceptively simple. The complexity arises from the "many-to-many-to-many" relationships:

This creates a combinatorial explosion. A query for "available content" isn't a simple SELECT * FROM works WHERE available = true. It's a multidimensional query across a graph of interconnected entities. The system must resolve conflicts, understand the hierarchy of agreements (a distribution deal might supersede a direct license), and handle temporal constraints (rights that expire or have blackout periods). This is where it gets technically interesting—it's less a standard CRUD app and more a specialized query engine for a high-dimension dataset.

Architectural Sketch: A Pragmatic Stack

Let's consider building a dashboard to manage this complexity. The goal is a system that allows a rights manager to quickly query "avails" (what's available to be licensed) and a finance team to accurately calculate and track royalties. A potential stack could look like this: MongoDB, Python (FastAPI), and Vue.js.

Data Modeling in MongoDB

This problem domain feels like a natural fit for a document database. A single legal agreement is a self-contained unit that bundles together parties, works, terms, and granted rights. Forcing this into a highly normalized SQL structure can lead to unwieldy joins. A simplified `agreements` collection in MongoDB might look like this:

{
  "_id": ObjectId("..."),
  "work_id": ObjectId("..."), // Ref to the 'works' collection
  "licensor_id": ObjectId("..."), // Ref to 'rightsholders'
  "licensee_id": ObjectId("..."),
  "term_start": ISODate("2024-01-01T00:00:00Z"),
  "term_end": ISODate("2026-12-31T23:59:59Z"),
  "granted_rights": [
    {
      "territories": ["US", "CA", "MX"], // ISO 3166-1 alpha-2
      "media_types": ["SVOD"],
      "exclusivity": "exclusive"
    },
    {
      "territories": ["WORLDWIDE_EXCLUDING", "CN", "RU"],
      "media_types": ["AVOD", "FAST"],
      "exclusivity": "non-exclusive"
    }
  ],
  "royalty_terms": {
    "type": "revenue_share",
    "rate": 0.25, // 25%
    "minimum_guarantee": 50000,
    "currency": "USD"
  }
}

This structure allows for rich, indexed queries directly against the dimensional data. The core "avails" check becomes a MongoDB aggregation pipeline that filters `agreements` by `work_id`, `term` dates, and then matches against the nested `granted_rights` arrays. Denormalization here is a conscious tradeoff for query performance.

Backend API and Frontend UX

A Python backend using FastAPI is a solid choice for its performance and type-hinting support, which is critical for maintaining correctness with these complex data structures. The star of the show would be the avails endpoint: GET /api/v1/avails?work_id=...&territory=US&media_type=SVOD&date=.... This endpoint would execute the complex database query and return a clear "available," "unavailable," or "conflicting" status.

On the frontend, Vue.js with Pinia for state management and TypeScript for type safety provides the structure needed for a reactive and complex UI. The primary view wouldn't be a simple table. It should be an interactive matrix: works as rows, and a combination of territories and media types as columns. Each cell would be color-coded based on the avails query result. Clicking a cell could pop up a modal showing the specific agreement(s) governing that right. This visual feedback is crucial for a user to quickly understand a work's licensing landscape.

For royalty calculations, which are often batch processes run on monthly usage reports, the Python backend can trigger idempotent jobs that read usage data, find the relevant `agreement`, apply the `royalty_terms`, and generate statement line items. Real-time updates for the dashboard, like the status of an ingestion job, could be pushed from the server to the client using Server-Sent Events (SSE), a simpler alternative to WebSockets when the data flow is primarily one-way.

Tradeoffs, Scale, and the Human-in-the-Loop

This architecture is not without its challenges. At scale, the avails query can become a performance bottleneck. Compound indexes in MongoDB on fields like work_id, term_start, term_end, and the fields within granted_rights are non-negotiable. For extremely high query volumes, a caching layer like Redis for popular avails checks might be necessary.

The biggest challenge, however, is correctness. A bug in the royalty calculation logic has direct financial consequences. An error in the avails logic can lead to double-licensing a right, a serious legal breach. This is where pragmatism trumps pure automation. The "chain of title" can be incredibly complex, with rights being sub-licensed multiple times. Modeling this might require moving from a simple document reference to a more explicit graph structure, or at least flagging these complex cases for manual review.

This brings us to the most critical aspect: data ingestion. Most rights agreements start their life as scanned PDF documents filled with legal jargon. Manually transcribing this into structured data is slow and error-prone. This is a perfect application for AI/LLMs, but not in a fully autonomous way.

A pragmatic workflow would be:

  1. An LLM-powered service (using a Python backend) parses the uploaded PDF, extracting key entities: licensor, licensee, term dates, territories, media types, and royalty percentages.
  2. The LLM outputs a structured JSON object—a draft of the agreement document shown above.
  3. The UI presents this draft to a human operator in a two-pane view: the original PDF on one side, and the editable data form pre-filled by the AI on the other.
  4. The operator verifies, corrects, and ultimately approves the structured data for insertion into the database.

This "human-in-the-loop" approach leverages the LLM for efficiency while retaining the human expert for correctness. For a system where legal and financial accuracy is paramount, this tradeoff is non-negotiable.

A Reflection on the Problem

Engineering a media rights and royalties platform is a compelling challenge because the core asset isn't the data itself, but the interpretation of that data according to complex, negotiated rules. The technical solution must be more than just a database; it must be an engine for encoding and querying these rules. The work involves deep data modeling, creating intuitive UIs for non-technical experts to navigate immense complexity, and pragmatically applying new technologies like LLMs where they can assist, not just automate. It's a problem space where robust, correct, and thoughtful engineering has a direct and significant impact on the business it serves.