> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Gateway Relationships

> How gateways discover and select your orchestrator – the discovery mechanisms, multi-factor selection algorithm, what you can do to receive more work, and how to use the gateway Loki API to understand routing decisions.

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </div>;
};

Gateways decide where work goes. Your node receives jobs only when it is discoverable, supports the requested workload, and fits the Gateway budget. Understanding that pipeline is what separates operators who receive steady work from those who are technically online but still idle.

<CustomDivider />

## How Gateways find you

There are four mechanisms by which a Gateway builds its list of Orchestrators to consider:

<CardGroup cols={2}>
  <Card title="On-chain discovery" icon="link">
    The Gateway queries the Livepeer subgraph on Arbitrum for all registered, active Orchestrators. Your on-chain registration – service URI, stake, cuts – makes you visible here automatically once you are in the Active Set.
  </Card>

  <Card title="Direct configuration" icon="server">
    Gateway operators can configure specific Orchestrators directly with `-orchAddr`. This bypasses the discovery process entirely. Private pools and enterprise setups often use direct configuration.
  </Card>

  <Card title="Webhook discovery" icon="webhook">
    Dynamic discovery via an external service: `-orchWebhookUrl=https://discovery.example.com/orchestrators`. The Gateway calls this URL to get a list of eligible Orchestrators, enabling custom filtering or whitelisting without on-chain registration.
  </Card>

  <Card title="Network Capabilities API" icon="broadcast-tower">
    The Gateway calls `GET /getNetworkCapabilities` to query what capabilities and models are available across the network. This is primarily used for AI workload routing.
  </Card>
</CardGroup>

**What this means for you:** For standard video transcoding, being registered on-chain and in the top 100 active Orchestrators is sufficient for discovery. For AI workloads, capability advertisement through your `aiModels.json` configuration is what makes you discoverable for specific pipelines.

<CustomDivider />

## What you advertise to Gateways

Every time a Gateway queries you, your node responds with an `OrchestratorInfo` message containing your full offering:

```protobuf icon="terminal" title="Example OrchestratorInfo fields" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
message OrchestratorInfo {
    string transcoder = 1;           // Your service URI
    TicketParams ticket_params = 2;   // Payment parameters
    PriceInfo price_info = 3;         // Your price per pixel
    bytes address = 4;               // Your ETH address
    Capabilities capabilities = 5;    // Transcoding profiles + AI pipelines
    AuthToken auth_token = 6;         // Session auth
    repeated HardwareInformation hardware = 7;  // GPU specs
    repeated PriceInfo capabilities_prices = 33; // Per-pipeline AI prices
}
```

The `capabilities` field declares what transcoding profiles and output formats you support. The `capabilities_prices` field contains your per-pipeline AI pricing. Both are built automatically from your go-livepeer configuration and `aiModels.json`.

<CustomDivider />

## How Gateways select you

Discovery gives a Gateway a list of candidates. Selection narrows that list to one (or a few) nodes that actually receive a given job. The selection algorithm is multi-factor:

```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
flowchart TD
    A["All registered orchestrators"] --> B["Capability match?\n(supports requested pipeline/resolution)"]
    B -->|No| X1["Excluded"]
    B -->|Yes| C["Price within limit?\n(below gateway maxPricePerUnit\nor maxPricePerCapability)"]
    C -->|No| X2["Excluded"]
    C -->|Yes| D["Blacklist check\n(recent failures)"]
    D -->|Blacklisted| X3["Excluded"]
    D -->|Clean| E["Score: performance +\nstake weight + latency +\nrandom factor"]
    E --> F["Selected orchestrator"]
```

**The five selection factors:**

<StyledTable variant="bordered">
  <TableRow header>
    <TableCell header>Factor</TableCell>
    <TableCell header>How it works</TableCell>
    <TableCell header>What you control</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Capability matching**</TableCell>
    <TableCell>Does your node support the requested pipeline, resolution, or AI model?</TableCell>
    <TableCell>`aiModels.json`, transcoding profiles</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Price constraints**</TableCell>
    <TableCell>Your price must be at or below the Gateway's configured maximum</TableCell>
    <TableCell>`-pricePerUnit` for transcoding; per-capability pricing for AI</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Performance score**</TableCell>
    <TableCell>Historical success rate, latency, and failure rate tracked per Orchestrator</TableCell>
    <TableCell>Uptime, hardware quality, network speed</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Stake weight**</TableCell>
    <TableCell>For video transcoding, higher stake increases selection probability (configured via Gateway `-selectStakeWeight`)</TableCell>
    <TableCell>Total stake (self + delegated)</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Random factor**</TableCell>
    <TableCell>Gateways introduce randomness (`-selectRandFreq`) to prevent centralisation</TableCell>
    <TableCell>Not directly controllable</TableCell>
  </TableRow>
</StyledTable>

**Practical implication for AI jobs:** Capability match and price dominate AI routing. Your stake is less important than for video transcoding. If you want AI jobs, ensure your pipeline is registered, your warm model is loaded, and your price is within market range.

**Practical implication for video jobs:** Price and performance score are the primary drivers after entering the Active Set. A competitive price and high success rate will consistently outperform a lower-ranked Orchestrator with a high price.

<CustomDivider />

## What you can control to get more work

<AccordionGroup>
  <Accordion title="1. Price competitively" icon="circle-question">
    Pricing is binary before it is graduated: if your price exceeds the Gateway's maximum, you receive zero work from that Gateway. After clearing the ceiling, lower prices increase your attractiveness.

    Check current market rates:

    * Compare your `-pricePerUnit` to other active Orchestrators on [Livepeer Explorer](https://explorer.livepeer.org/orchestrators)
    * For AI, check [tools.Livepeer.cloud](https://tools.livepeer.cloud) to see per-pipeline pricing from other operators

    The Gateway-side flags that affect your eligibility: `-maxPricePerUnit` for transcoding and `-maxPricePerCapability` (a JSON structure) for AI pipelines. You cannot see what individual Gateways have set these to – but you can infer from the market.

    See [Pricing Strategy](/v2/Orchestrators/guides/config-and-optimisation/pricing-strategy) for how to adjust your prices.
  </Accordion>

  <Accordion title="2. Keep your service URI correct and reachable" icon="circle-question">
    If Gateways cannot connect to your service URI, you receive no work – even if you are in the Active Set, even if your price is competitive.

    **Test reachability:**

    ```bash icon="terminal" title="Test service URI reachability" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    # From a machine outside your network
    curl -v https://<your-service-uri>:8935/status
    ```

    Common causes of unreachability: IP changed without on-chain update, firewall change blocking port 8935, certificate issue on TLS endpoint, NAT not forwarding correctly.

    Update your on-chain service URI via `livepeer_cli` if your IP or hostname has changed.
  </Accordion>

  <Accordion title="3. Register your AI capabilities correctly" icon="microchip">
    For AI jobs, capability registration is the prerequisite. Your pipelines and warm models must be visible to the network.

    **Verify:**

    ```bash icon="terminal" title="Check registered capabilities" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    curl http://localhost:7935/getNetworkCapabilities | jq
    ```

    Also check externally:

    * [tools.Livepeer.cloud/ai/network-capabilities](https://tools.livepeer.cloud/ai/network-capabilities) shows all AI-capable Orchestrators visible to the network and which models are warm

    If your pipeline is missing from both, check your `aiModels.json` and confirm the AI Runner container started successfully.
  </Accordion>

  <Accordion title="4. Maintain high performance scores" icon="microchip">
    Performance scoring is based on your historical success rate and latency. Missed segments, slow responses, and OOM failures all decrease your score and reduce your selection probability.

    What drives good performance: stable hardware, sufficient VRAM, fast network, and consistent uptime. A node that fails 5% of its segments will eventually score lower than a node with identical pricing but near-zero failures.
  </Accordion>

  <Accordion title="5. Build stake for video transcoding" icon="video">
    For video transcoding, selection probability is weighted by total stake. Being in the top 10 by stake vs top 50 means meaningfully more job volume from stake-weighted Gateways.

    For AI workloads, stake is less decisive. Capability and price are the primary routing criteria.
  </Accordion>
</AccordionGroup>

<CustomDivider />

## Gateway Loki API – understanding selection decisions

The Livepeer Foundation operates a public Loki instance that exposes Gateway logs. This API lets you see what is happening inside Gateway nodes – including why specific Orchestrators were or were not selected.

**Base URL:** `https://loki.livepeer.report`

```bash icon="terminal" title="Query gateway Loki logs" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# All gateway logs from all regions
curl -G -s https://loki.livepeer.report/loki/api/v1/query \
     --data-urlencode "query={region=~\".+\"}" | jq

# Logs from a specific region (e.g. NYC)
curl -G -s https://loki.livepeer.report/loki/api/v1/query \
     --data-urlencode "query={region=~\"nyc\"}" | jq

# Logs related to your specific orchestrator IP
curl -G -s https://loki.livepeer.report/loki/api/v1/query \
     --data-urlencode "query={region=~\".+\"} |= \"clientIP=<YOUR_IP>\"" | jq

# Logs between two timestamps (UNIX epoch in nanoseconds)
curl -G -s https://loki.livepeer.report/loki/api/v1/query_range \
     --data-urlencode "query={region=~\".+\"}" \
     --data 'start=1727335380000000000' \
     --data 'end=1727635380000000000' | jq

# List all available gateway regions
curl -G -s https://loki.livepeer.report/loki/api/v1/label/region/values | jq '.data'
```

**What to look for:**

* Selection events including or excluding your Orchestrator address
* Price rejection messages (your price exceeding the Gateway maximum)
* Capability mismatch messages (requested pipeline not found in your offerings)
* Connection failures (Gateway could not reach your service URI)

The API uses [Grafana Loki query syntax](https://grafana.com/docs/loki/latest/query/). You can pipe output to `jq` for readable formatting.

<CustomDivider />

## Debugging missing jobs

Use this checklist when you are in the Active Set and job flow stays at zero:

```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
flowchart TD
    A["In active set but no jobs"] --> B{"Service URI reachable?\ncurl -v https://uri:8935/status"}
    B -->|No| C["Fix: update IP/firewall\nupdate on-chain URI"]
    B -->|Yes| D{"Price within gateway limits?\nCompare to Explorer market rates"}
    D -->|Too high| E["Fix: lower -pricePerUnit\nor per-capability prices"]
    D -->|Competitive| F{"Capabilities registered?\ncurl localhost:7935/getNetworkCapabilities"}
    F -->|Missing| G["Fix: check aiModels.json\nrestart AI runner"]
    F -->|Present| H{"Is network demand low?\nCheck Dune/Explorer for network activity"}
    H -->|Low demand| I["Wait — network demand fluctuates"]
    H -->|Normal demand| J["Check Loki API for gateway\nselection logs"]
```

<CustomDivider />

<CardGroup cols={2}>
  <Card title="Configure Pricing" icon="tag" href="/v2/orchestrators/setup/configure">
    Setting pricePerUnit and per-capability AI pricing to be competitive.
  </Card>

  <Card title="AI Configuration" icon="robot" href="/v2/orchestrators/guides/ai-and-job-workloads/ai-inference-operations">
    Setting up aiModels.json and capability registration.
  </Card>

  <Card title="Troubleshooting" icon="triangle-exclamation" href="/v2/orchestrators/guides/monitoring-and-tooling/troubleshooting">
    Full error reference including service URI and capability issues.
  </Card>

  <Card title="Orchestrator Tools" icon="toolbox" href="/v2/orchestrators/guides/monitoring-and-tooling/operator-toolbox">
    Explorer, Prometheus, and Loki tools for understanding network state.
  </Card>
</CardGroup>
