> ## 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.

# Payment Receipts

> How probabilistic micropayment tickets arrive, when they win, how the Redeemer submits them on-chain, and how to verify ticket redemption is working correctly.

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>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

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>;
};

<Tip>
  ETH earnings arrive in discrete redemption steps whenever a winning ticket is redeemed on-chain. A node that processed thousands of jobs often shows zero ETH balance changes for hours, then receives a larger deposit. This is expected behaviour and part of the PM design.
</Tip>

***

Gateways pay Orchestrators using **Probabilistic Micropayments (PM)**: a system where each job generates a signed ticket with a small probability of being worth a larger ETH payout. Most tickets are non-winners and are discarded. A fraction win and are redeemed on-chain via the Arbitrum TicketBroker contract. Over sufficient volume, the expected ETH received matches the per-segment total while avoiding the gas overhead of thousands of individual on-chain payments.

<CustomDivider />

## How tickets arrive

A Gateway attaches one PM ticket to each job confirmation it sends after receiving the transcoded output or AI inference result. The ticket is received passively as part of the job confirmation flow. The flow is:

```text icon="terminal" title="Payment receipt flow" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
Your node processes job (transcoding segment or AI inference)
       ↓
Your node sends the result to the gateway
       ↓
Gateway sends a signed PM ticket back to your node
       ↓
Your node evaluates the ticket: is it a winner?
       ↓
Non-winning tickets discarded immediately
Winning tickets queued for the Redeemer
       ↓
Redeemer submits winning tickets to Arbitrum TicketBroker
       ↓
ETH credited to your orchestrator wallet
```

### Ticket structure

Each ticket contains the fields the Arbitrum contract uses to verify and pay:

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Field</TableCell>
      <TableCell header>Description</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>**Face value**</TableCell>
      <TableCell>ETH amount payable on a winning ticket</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Win probability**</TableCell>
      <TableCell>Probability the ticket is a winner (e.g. `1e-5`)</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Sender address**</TableCell>
      <TableCell>The Gateway's Ethereum address</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Recipient address**</TableCell>
      <TableCell>Your Orchestrator's Ethereum address</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Sender deposit**</TableCell>
      <TableCell>The Gateway's on-chain deposit that backs the ticket</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Expiry**</TableCell>
      <TableCell>Block number after which the ticket cannot be redeemed</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Signature**</TableCell>
      <TableCell>Gateway's cryptographic signature authorising the payment</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

Your node verifies the signature and checks that the Gateway has sufficient on-chain deposit to back the ticket before evaluating the win condition.

<CustomDivider />

## Win condition

The win evaluation uses a deterministic pseudorandom function seeded by the ticket contents. Each ticket carries a `winProbability` parameter (e.g. `1/1000`). The evaluator generates a random number from the ticket data and the current block hash, then checks whether the output falls below the win threshold.

The expected value of a single ticket equals the per-job fee:

```text icon="terminal" title="Winning ticket expected value example" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
win probability = 1/1000
face value      = 1000 × per-job fee
expected value  = 1/1000 × 1000 × fee = fee per job
```

Over a large number of tickets, the ETH received converges to the equivalent per-job total. At low job volumes, the variance is high – a node processing 10 jobs per day might see zero redemptions some days and two on others. Variance decreases as volume increases.

<CustomDivider />

## The Redeemer

The Redeemer is the go-livepeer component that submits winning tickets to the Arbitrum TicketBroker contract. On a standard single-node Orchestrator, it runs as part of the main process and requires no separate configuration.

### Redemption cost

Redemption transactions run on Arbitrum One (L2). Gas costs are low:

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Parameter</TableCell>
      <TableCell header>Typical range</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>Gas per redemption transaction</TableCell>
      <TableCell>\~100,000 – 200,000 gas</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Arbitrum gas price</TableCell>
      <TableCell>0.01 – 0.1 gwei</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Approximate ETH cost per redemption</TableCell>
      <TableCell>\~$0.01 – $0.05 USD</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

The Redeemer batches pending winners when possible. A busy Orchestrator usually redeems ETH in a few batched submissions per day, keeping gas overhead negligible relative to earnings.

### ETH balance requirement

Your Orchestrator wallet on Arbitrum must hold ETH to pay redemption gas. **Maintain a minimum of 0.01 ETH on Arbitrum.** A depleted wallet means winning tickets expire unsubmitted – that ETH is permanently lost.

<Warning>
  Winning tickets carry an expiry block number. A wallet that runs out of ETH before submission loses expired tickets permanently. The Gateway's deposit is unaffected; the loss falls entirely on your side.
</Warning>

Replenish when your balance falls below 0.005 ETH on Arbitrum. Bridge ETH from Ethereum L1 using the [Arbitrum Bridge](https://bridge.arbitrum.io/) or a centralised exchange withdrawal.

<CustomDivider />

## Monitoring payment receipts

### Expected payment rate

Your expected ETH per round is:

```text icon="terminal" title="Expected ETH estimate" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
expected ETH = jobs_per_round × per_job_fee × (1 - gas_overhead)
```

At low job volumes, actual ETH received in a given round will diverge from this estimate. The convergence improves with volume. A node processing under 50 jobs per day should expect high variability between rounds.

### Verifying redemption is working

Check your Orchestrator wallet on [Arbiscan](https://arbiscan.io/) for incoming transactions from the TicketBroker contract. Each successful redemption appears as an ETH credit.

From your node logs, filter for redemption activity:

```bash icon="terminal" title="Watch redemption logs" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Watch for successful redemptions
journalctl -u livepeer -f | grep -i "ticket\|redeem\|winning"
```

Key log messages:

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Log message</TableCell>
      <TableCell header>Meaning</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>`Winning ticket`</TableCell>
      <TableCell>Ticket passed the win condition and entered the Redeemer queue</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>`Redeemed ticket`</TableCell>
      <TableCell>Submission to Arbitrum succeeded; ETH credited</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>`Failed to redeem ticket`</TableCell>
      <TableCell>Submission failed; check ETH balance and RPC connectivity</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>`Ticket expired`</TableCell>
      <TableCell>Winning ticket reached expiry before submission; ETH permanently lost</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

### Zero earnings for a long period

Possible causes:

* **Low job volume** – at fewer than \~20 jobs per day, zero-win rounds are statistically common.
* **ETH balance depleted** – winning tickets fail to redeem silently when gas cannot be paid.
* **RPC connectivity issue** – check your `-ethUrl` endpoint is reachable and within provider limits.
* **Node offline during job routing** – verify your node is connected and registered in the Active Set.
* **Price above Gateway caps** – confirm your `-pricePerUnit` is within range of active Gateways. See <LinkArrow href="/v2/orchestrators/guides/config-and-optimisation/pricing-strategy" label="Pricing Strategy" newline={false} />.

### Overlapping scope with payments.mdx

Use this page for the receiver-side mechanics of PM tickets: how they arrive, how they win, and how redemption works. The <LinkArrow href="/v2/orchestrators/guides/payments-and-pricing/payments" label="Payments" newline={false} /> page covers the broader payment flow concepts, including full PM architecture, ETH funding requirements across transaction types, video vs AI payment differences, and clearinghouses. Read both for the complete picture.

<CustomDivider />

## Related pages

<CardGroup cols={2}>
  <Card title="Payments" icon="coins" href="/v2/orchestrators/guides/payments-and-pricing/payments" arrow horizontal>
    Full PM architecture, ETH funding, video vs AI payment differences, and clearinghouses.
  </Card>

  <Card title="Gateway Payment Guide" icon="code-branch" href="/v2/gateways/guides/payments-and-pricing/payment-guide" arrow horizontal>
    The sender-side view: how Gateways fund deposits and sign tickets.
  </Card>

  <Card title="Pricing Strategy" icon="tag" href="/v2/orchestrators/guides/config-and-optimisation/pricing-strategy" arrow horizontal>
    Set prices within active Gateway cap ranges to ensure tickets arrive.
  </Card>

  <Card title="Reward Mechanics" icon="rotate" href="/v2/orchestrators/guides/staking-and-rewards/reward-mechanics" arrow horizontal>
    LPT inflation rewards – the second income stream alongside ETH fees.
  </Card>
</CardGroup>
