Omnigraph
Guides

Revenue Operations

Build an AI-powered revenue operations layer — pipeline intelligence, account scoring, deal risk, and rep coaching — all backed by a typed knowledge graph.

Your CRM holds records. Your data warehouse holds metrics. Your agents need a world model that connects them: accounts to signals, signals to deals, deals to people, people to interactions. That's a graph.

This guide walks through building a RevOps intelligence layer where specialized agents ingest signals, score accounts, flag deal risk, and surface coaching opportunities — each on its own branch, each auditable, each mergeable.

The ontology

node Account {
    slug: String @key
    name: String @index
    domain: String? @unique
    tier: enum(enterprise, mid_market, smb)
    industry: enum(saas, fintech, health, infra, consumer, other)
    arr: F64?
    employee_count: I32?
    health_score: F64?
}

node Contact {
    slug: String @key
    name: String @index
    title: String? @index
    email: String? @unique
    seniority: enum(c_level, vp, director, manager, ic)
    persona: enum(champion, evaluator, blocker, economic_buyer, end_user)
}

node Deal {
    slug: String @key
    name: String @index
    stage: enum(prospecting, discovery, evaluation, negotiation, closed_won, closed_lost)
    amount: F64?
    close_date: Date?
    risk: enum(low, medium, high, critical)?
    created_at: Date
}

node Activity {
    slug: String @key
    type: enum(email, call, meeting, demo, proposal, follow_up)
    subject: String @index
    body: String? @index
    sentiment: enum(positive, neutral, negative)?
    occurred_at: DateTime
    embedding: Vector(768)? @index
}

node Signal {
    slug: String @key
    type: enum(product_usage, intent_data, news, job_change, funding, churn_indicator, expansion_signal)
    title: String @index
    body: String?
    strength: enum(strong, moderate, weak)
    source: String
    detected_at: DateTime
}

node Rep {
    slug: String @key
    name: String @index
    role: enum(ae, sdr, csm, se, manager)
    team: String
}

// Relationships
edge BelongsTo: Contact -> Account
edge Owns: Rep -> Deal @card(1..1)
edge Involves: Deal -> Account @card(1..1)
edge Participants: Deal -> Contact
edge Performed: Rep -> Activity
edge Regarding: Activity -> Contact
edge AboutDeal: Activity -> Deal
edge Indicates: Signal -> Account
edge TriggeredDeal: Signal -> Deal

// Competitive & organizational
edge ReportsTo: Contact -> Contact @card(0..1)
edge Champions: Contact -> Deal @card(0..1)

Read this schema and the domain is self-evident. An agent seeing this ontology immediately understands: accounts have contacts, reps own deals, deals involve accounts, activities connect reps to contacts about deals, and signals flow from external sources into accounts and deals.

Load your data

{"type": "Account", "data": {"slug": "acme-corp", "name": "Acme Corp", "domain": "acme.com", "tier": "enterprise", "industry": "saas", "arr": 240000, "employee_count": 850}}
{"type": "Account", "data": {"slug": "globex", "name": "Globex Inc", "domain": "globex.io", "tier": "mid_market", "industry": "fintech", "arr": 48000, "employee_count": 120}}
{"type": "Contact", "data": {"slug": "jane-cto", "name": "Jane Torres", "title": "CTO", "email": "jane@acme.com", "seniority": "c_level", "persona": "champion"}}
{"type": "Contact", "data": {"slug": "mike-vpe", "name": "Mike Chen", "title": "VP Engineering", "email": "mike@acme.com", "seniority": "vp", "persona": "evaluator"}}
{"type": "Contact", "data": {"slug": "sarah-cfo", "name": "Sarah Kim", "title": "CFO", "email": "sarah@acme.com", "seniority": "c_level", "persona": "economic_buyer"}}
{"type": "Rep", "data": {"slug": "alex-ae", "name": "Alex Rivera", "role": "ae", "team": "enterprise-west"}}
{"type": "Deal", "data": {"slug": "acme-expansion", "name": "Acme Platform Expansion", "stage": "negotiation", "amount": 480000, "close_date": "2026-04-30", "risk": "medium", "created_at": "2026-01-15"}}
{"edge": "BelongsTo", "from": "jane-cto", "to": "acme-corp", "data": {}}
{"edge": "BelongsTo", "from": "mike-vpe", "to": "acme-corp", "data": {}}
{"edge": "BelongsTo", "from": "sarah-cfo", "to": "acme-corp", "data": {}}
{"edge": "Involves", "from": "acme-expansion", "to": "acme-corp", "data": {}}
{"edge": "Owns", "from": "alex-ae", "to": "acme-expansion", "data": {}}
{"edge": "Champions", "from": "jane-cto", "to": "acme-expansion", "data": {}}
{"edge": "Participants", "from": "acme-expansion", "to": "jane-cto", "data": {}}
{"edge": "Participants", "from": "acme-expansion", "to": "mike-vpe", "data": {}}
{"type": "Signal", "data": {"slug": "acme-hiring-surge", "type": "expansion_signal", "title": "Acme posted 12 engineering roles in 2 weeks", "strength": "strong", "source": "linkedin-monitor", "detected_at": "2026-03-25T10:00:00"}}
{"edge": "Indicates", "from": "acme-hiring-surge", "to": "acme-corp", "data": {}}
{"edge": "TriggeredDeal", "from": "acme-hiring-surge", "to": "acme-expansion", "data": {}}
omnigraph init --schema revops.pg ./revops-graph
omnigraph load ./revops-graph --data revops.jsonl

Queries your agents run

Pipeline risk dashboard

An analyst agent assesses which deals need attention:

query at_risk_pipeline() {
    match {
        $d: Deal
        $d.stage != "closed_won"
        $d.stage != "closed_lost"
        $d involves $a
        $rep owns $d
    }
    return {
        $d.name, $d.stage, $d.amount, $d.close_date, $d.risk,
        $a.name as account, $a.tier,
        $rep.name as owner
    }
    order { $d.amount desc }
}
[
  {
    "name": "Acme Platform Expansion",
    "stage": "negotiation",
    "amount": 480000,
    "close_date": "2026-04-30",
    "risk": "medium",
    "account": "Acme Corp",
    "tier": "enterprise",
    "owner": "Alex Rivera"
  }
]

Missing champion detection

Deals without a champion are 3x more likely to stall:

query deals_without_champion() {
    match {
        $d: Deal
        $d.stage != "closed_won"
        $d.stage != "closed_lost"
        $d.amount > 100000
        $d involves $a
        not { $_ champions $d }
    }
    return { $d.name, $d.stage, $d.amount, $a.name as account }
    order { $d.amount desc }
}

Good — the Acme deal has Jane Torres as champion. If she leaves or goes silent, the deal shows up here immediately.

Contact gap analysis

Which enterprise accounts are missing economic buyer engagement?

query accounts_without_economic_buyer() {
    match {
        $a: Account { tier: "enterprise" }
        $d involves $a
        $d.stage != "closed_won"
        $d.stage != "closed_lost"
        not {
            $d participants $c
            $c.persona = "economic_buyer"
        }
    }
    return { $a.name, $d.name, $d.stage, $d.amount }
    order { $d.amount desc }
}

An SDR agent reads this result and knows exactly where to focus outreach.

Signal-enriched deal context

When a rep opens a deal, give them everything — the account, the contacts, the signals, the activity trail:

query deal_briefing($deal: String) {
    match {
        $d: Deal { slug: $deal }
        $d involves $a
        $d participants $c
        $s triggeredDeal $d
    }
    return {
        $d.name, $d.stage, $d.amount, $d.close_date,
        $a.name as account, $a.arr, $a.tier,
        $c.name as contact, $c.title, $c.persona,
        $s.title as signal, $s.type as signal_type, $s.strength
    }
}
[
  {
    "name": "Acme Platform Expansion",
    "stage": "negotiation",
    "amount": 480000,
    "close_date": "2026-04-30",
    "account": "Acme Corp",
    "arr": 240000,
    "tier": "enterprise",
    "contact": "Jane Torres",
    "title": "CTO",
    "persona": "champion",
    "signal": "Acme posted 12 engineering roles in 2 weeks",
    "signal_type": "expansion_signal",
    "strength": "strong"
  }
]

One query. Full deal context. Typed and traceable.

Engagement velocity

How recently has the rep engaged with deal contacts?

query recent_activity($deal: String) {
    match {
        $d: Deal { slug: $deal }
        $act aboutDeal $d
        $rep performed $act
        $act regarding $contact
    }
    return {
        $act.type, $act.subject, $act.sentiment, $act.occurred_at,
        $rep.name as rep,
        $contact.name as contact, $contact.persona
    }
    order { $act.occurred_at desc }
    limit 20
}

An engagement scoring agent reads this and computes: days since last champion contact, ratio of positive to negative sentiment, meeting cadence. All from graph traversal.

Find past interactions about specific topics:

query find_relevant_conversations($deal: String, $topic: Vector(768)) {
    match {
        $d: Deal { slug: $deal }
        $act aboutDeal $d
    }
    return { $act.subject, $act.type, $act.occurred_at }
    order { nearest($act.embedding, $topic) }
    limit 5
}

"What conversations has the rep had about pricing?" — structure scopes to this deal, vector similarity finds the semantic match.

Multi-agent architecture

                         +----------+
                         |   main   |
                         +----+-----+
                              |
          +-------------------+-------------------+
          |                   |                   |
   +------+------+     +-----+------+    +-------+-------+
   | signal-agent|     | risk-agent |    | coaching-agent|
   |             |     |            |    |               |
   | Reads:      |     | Reads:     |    | Reads:        |
   | - RSS feeds |     | - Deals    |    | - Activities  |
   | - LinkedIn  |     | - Signals  |    | - Deal stage  |
   | - Intent    |     | - Contacts |    | - Contact     |
   |             |     |            |    |   engagement  |
   | Writes:     |     | Writes:    |    |               |
   | - Signal    |     | - Deal.risk|    | Writes:       |
   | - Indicates |     | - Account  |    | - Coaching    |
   | - Triggered |     |   .health  |    |   notes       |
   +-------------+     +------------+    +---------------+

Each agent works on its own branch:

# Signal agent ingests external data
omnigraph branch create ./revops-graph signal-ingest-20260330
# ... agent writes Signals, Indicates edges, TriggeredDeal edges
omnigraph branch merge ./revops-graph --source signal-ingest-20260330 --into main

# Risk agent scores deals
omnigraph branch create ./revops-graph risk-scoring-20260330
# ... agent reads deals + signals, updates Deal.risk
omnigraph branch merge ./revops-graph --source risk-scoring-20260330 --into main

# Coaching agent surfaces insights
omnigraph branch create ./revops-graph coaching-20260330
# ... agent reads activity patterns, writes coaching notes
omnigraph branch merge ./revops-graph --source coaching-20260330 --into main

The signal agent doesn't know about the risk agent. The risk agent doesn't know about the coaching agent. They coordinate through the graph: signals appear, risk scores update, coaching insights reference the risk context. No orchestrator.

What you can build from here

  • Forecast agent — Read deal stages, amounts, close dates, and signal strength. Predict weighted pipeline and flag deals where velocity has dropped.
  • Territory mapping — Add Region nodes and Covers: Rep -> Region edges. Auto-balance territories by querying account distribution and deal load per rep.
  • Competitive intelligence — Add Competitor nodes and CompetesWith: Deal -> Competitor edges. Track win/loss patterns per competitor.
  • Renewal risk — Combine ARR data, support ticket volume, product usage signals, and contact engagement to predict churn before the renewal date.

On this page