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

# Agent Pay and Agent Compensation Flags

> How web and iOS clients show who gets paid for a deal, and how the agent-wide Agent Pay view is loaded. Integration reference for ENG-1413.

A deal involving an agent can be in one of three arrangements, and until now the clients had no reliable way to tell them apart. This page is the client-side contract for ENG-1413: the fields that say who is being paid, and the endpoint behind the agent-wide Agent Pay view.

The backend work is complete and deployed on the feature branch. Nothing on this page requires a client to compute or infer the arrangement — the server decides and the client displays.

## The three arrangements

<CardGroup cols={3}>
  <Card title="No agent" icon="user">
    The athlete was not represented when they applied. MOGL pays the athlete, and no agent is involved in the deal at all.
  </Card>

  <Card title="Represented, agent paid" icon="briefcase">
    MOGL pays the agent through Stripe Express, after retaining the agent commission. The agent settles with the athlete outside MOGL.
  </Card>

  <Card title="Represented, athlete paid" icon="graduation-cap">
    The Notre Dame arrangement. MOGL pays the athlete. The athlete settles with the agent outside MOGL.
  </Card>
</CardGroup>

<Warning>
  Who pressed Apply decides nothing. An athlete who applies for themselves while signed with an agent is an agent deal, and the agent is paid unless the brand pays athletes directly. Do not treat self-applied as "no agent", and do not read whether an agent is linked to the athlete today.
</Warning>

Representation and payment destination are two separate questions. An agent can represent a deal and the athlete still receive the money, so never derive one flag from the other.

Both answers are frozen when the application is created. Linking an agent later, unlinking one, or a brand flipping its payout setting does not change any deal that already exists. This is why the clients must read these fields rather than looking at the athlete's current agent.

## Compensation flags on deal and payment responses

Four fields were added to the responses the clients already call. No new request parameters, no version bump — existing calls simply return more fields.

| Field                  | Type                     | Meaning                                                            |
| ---------------------- | ------------------------ | ------------------------------------------------------------------ |
| `is_agent_represented` | boolean                  | An agent was representing the athlete on this deal                 |
| `payout_recipient`     | `"Athlete"` \| `"Agent"` | Who receives the deal money                                        |
| `agent_gets_paid`      | boolean                  | True only when `payout_recipient` is `Agent`                       |
| `agent_name`           | string \| null           | The representing agent, whether or not they are the one being paid |

### Where the flags appear

| Endpoint                      | Used by                        | Notes                                                                         |
| ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------- |
| `POST /myDealsV2`             | iOS My Deals, iOS Deal Details | Pass `job_id` for a single deal                                               |
| `POST /myDeals`               | Legacy iOS My Deals            | Same flags                                                                    |
| `POST /userJobsV2`            | Athlete web My Deals           | Same flags                                                                    |
| `POST /paymentHistoryAll`     | Athlete Pay                    | Job rows carry real flags; referral and fan rows return the no-agent defaults |
| `POST /paymentHistoryAthlete` | Athlete Pay, Pay detail        | Also returns `payment_received_by` and `gross_amount` for paid rows           |

<Note>
  Fan requests and referral payments always return `is_agent_represented: false`, `payout_recipient: "Athlete"`, `agent_gets_paid: false`, `agent_name: null`. The shape is identical across every row so the clients do not need a special case.
</Note>

### Which amount to show the athlete

Athlete Pay screens show what the athlete earned on the deliverable, never the transfer that was made to the agent.

| Endpoint                      | Field to display                           | Field not to display |
| ----------------------------- | ------------------------------------------ | -------------------- |
| `POST /paymentHistoryAll`     | `amount` — already the deliverable's price | —                    |
| `POST /paymentHistoryAthlete` | `gross_amount`                             | `amount_paid`        |

On `paymentHistoryAthlete` the two are equal whenever the athlete was paid directly, so the difference only shows up on deals paid to an agent: there `amount_paid` is the agent's net after commission. `gross_amount` was added for this reason and is the one to bind to.

<Warning>
  `minAmount` and `maxAmount` on `paymentHistoryAthlete` still filter on `amount_paid`. On a deal paid to an agent that is a different number from the one displayed, so a filtered list can look inconsistent. Raise it if the Pay filters need to match the displayed amount.
</Warning>

### What to show

For the badge in list and detail views, `is_agent_represented` is the one that answers "is an agent involved in this deal", and `agent_name` names them. For anything about money, use `agent_gets_paid`.

The two combine into three states. What gets said about each one depends on the surface, which the next section sets out.

| Flags                                                  | The state                                              |
| ------------------------------------------------------ | ------------------------------------------------------ |
| `is_agent_represented: false`                          | No agent on this deal                                  |
| `is_agent_represented: true`, `agent_gets_paid: true`  | An agent handled it and received the money             |
| `is_agent_represented: true`, `agent_gets_paid: false` | An agent handled it and the athlete received the money |

### Approved athlete-facing copy

My Deals and Pay answer different questions, so they use different flags. Getting this backwards is the most likely mistake here.

| Surface                                        | Flag                   | Shown when                                                                             |
| ---------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------- |
| My Deals `Agent` badge, collapsed and expanded | `is_agent_represented` | An agent represented the application, **including when the athlete was paid directly** |
| Pay `To Agent` badge                           | `agent_gets_paid`      | Only when that payment was directed to the agent                                       |

An athlete-direct payment uses the ordinary Pay row: no badge, and no mention of the agent, even though the athlete was represented. The representation is communicated on My Deals, not on Pay.

Copy, with `{AgentName}` from `agent_name`:

| Where                   | Condition                    | Copy                                                                     |
| ----------------------- | ---------------------------- | ------------------------------------------------------------------------ |
| My Deals, expanded deal | `is_agent_represented: true` | Agent {AgentName} is handling this deal with you                         |
| Pay row                 | `agent_gets_paid: true`      | Payment for this {amount} deliverable was sent to your Agent {AgentName} |

<Note>
  The Pay string names the deliverable's gross amount rather than saying a payment of that amount was made, because the agent received the net. Do not word it as though the gross was transferred.
</Note>

<Warning>
  The My Deals treatment needs `agent_name` on a deal the athlete was paid for. That is new: `agent_name` used to be null whenever the athlete received the money, which made this impossible to render. Do not fall back to looking up the athlete's current agent to fill it — that is the lookup this whole change exists to remove.
</Warning>

### Athlete-facing amounts

Athlete surfaces show the **gross** deal compensation only. The MOGL deduction and the net that went to the agent stay on the agent side. Do not compute what the athlete's own share of an agent payment was; MOGL does not know what the agent later paid them.

### Total Pay to Date caption

`athletePaymentTotal` returns a new `includes_agent_payments` boolean, at the top level and inside `data` alongside `total`.

`total` is gross compensation for completed payments, which already includes deals the agent was paid for. That is what makes the caption necessary: the number alone cannot say that some of the money went somewhere else.

| `includes_agent_payments` | Caption                                                        |
| ------------------------- | -------------------------------------------------------------- |
| `true`                    | Includes payments to your Agent<br />Excludes pending payments |
| `false`                   | The existing pending-exclusion caption                         |

Do not try to work this out from the payment history list. That list is paginated, so an agent-directed row may not be on the page you have. The flag is computed over exactly the payments the total summed, from the payout records, so it stays correct after a representation ends.

## Agent Pay view

```http theme={"dark"}
GET /api/agent/pay-feed
```

The agent-wide list of every deal this agent represents, whoever the money reached. It replaces the old athlete-scoped Payment History page for agents.

Rows where the athlete was paid directly are included and read `amount_to_agent: 0`. They are in the feed on purpose: leaving them out told an agent that a deal they handled did not exist.

<Warning>
  Do not send an athlete ID, and do not read `localStorage.selectedAthlete`. The agent comes from the access token. This endpoint ships with ENG-1379, which removes the header athlete selector, so any code depending on a selected athlete will break.
</Warning>

**Authorization:** requires an authenticated Agent (`role_id` 4). A caller with any other role receives `403`. There is no way to request another agent's feed.

### Query parameters

| Parameter                   | Default | Description                                                                                                                       |
| --------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `limit`                     | `10`    | Rows per page                                                                                                                     |
| `page`                      | `1`     | Standard Laravel pagination                                                                                                       |
| `status`                    | —       | One of `Payment Requested`, `Processing`, `Paid`                                                                                  |
| `search_keyword`            | —       | Matches deal name or athlete name                                                                                                 |
| `min_amount` / `max_amount` | —       | Bounds `gross_amount`, the deal value the row leads with                                                                          |
| `payment_mode`              | —       | `Stripe`, `PayPal` or `Paid Manually`. Send `All` for All Modes. Only paid rows carry a mode, so picking one drops in-flight rows |
| `paymentDate`               | —       | Matches the row's most recent activity date                                                                                       |

These are the same parameter names the athlete Pay endpoints accept, so one filter bar drives both views.

Rows come back newest first, with a deterministic tie-breaker. Pagination, filtering, and ordering all happen server-side.

### Response

Standard Laravel resource collection with `data`, `links`, and `meta`.

```json theme={"dark"}
{
  "data": [
    {
      "payout_id": 1692,
      "transaction_id": 154880,
      "athlete_id": 15901,
      "athlete_name": "Jordan Reyes",
      "athlete_profile_image": "storage/images/profile_photo/abc.png",
      "job_id": 2104,
      "job_name": "Spring Campaign",
      "partner_name": "Example Collective",
      "deliverable_id": 5312,
      "deliverable_name": "1st Deliverable",
      "deliverable_type": "Instagram Post",
      "gross_amount": 100,
      "amount_to_agent": 95,
      "commission_amount": 5,
      "commission_percentage": 5,
      "agent_commission_amount": 5,
      "net_amount": 95,
      "currency": "USD",
      "payout_status": "Paid",
      "payout_created_at": "2026-08-14 09:12:03",
      "payout_arrival_date": "2026-08-18 00:00:00",
      "payment_mode": "Stripe",
      "payment_received_on_account_type": "checking",
      "payment_received_on_detail": "4321",
      "transfer_reference": "tr_1QiC2qH7wGo4cnrNzJaTk4z8",
      "remark": "Payment successfully transferred",
      "payout_recipient": "Agent"
    }
  ]
}
```

### Reading the money fields

This is the part most likely to be displayed wrongly, so it is worth being precise.

| Field                     | In the example | What it is                                     |
| ------------------------- | -------------- | ---------------------------------------------- |
| `gross_amount`            | `100`          | The deal's compensation                        |
| `amount_to_agent`         | `95`           | What reached, or will reach, the agent         |
| `commission_amount`       | `5`            | Everything MOGL retained from the agent        |
| `agent_commission_amount` | `5`            | The agent's own commission                     |
| `commission_percentage`   | `5`            | The rate that produced it                      |
| `net_amount`              | `95`           | Same as `amount_to_agent` on an agent-paid row |

The agent's commission is the only thing taken out of the gross, so `commission_amount` and `agent_commission_amount` agree and `commission_percentage` applied to `gross_amount` produces both.

<Warning>
  The platform fee a brand pays on its deal is **not** part of these figures. A brand paying `$12.20` for a `$10` deliverable carries the `$2.20` itself, so the agent's row reads `gross_amount: 10` and `commission_amount: 1` at a 10% rate, not `12.20` and `3.20`. Back Office reports the brand's side of that separately as Amount Received and MOGL Fees.
</Warning>

### Athlete-direct rows

On a row where `payout_recipient` is `"Athlete"`, MOGL paid the athlete and kept nothing from the agent. Those rows carry:

* `gross_amount` — the deal value, the same figure an agent-paid row leads with
* `amount_to_agent` — `0`
* `commission_amount`, `agent_commission_amount`, `commission_percentage`, `net_amount` — all `null`, because there was no agent payout to take a commission from

The destination fields describe where the money actually went, which on these rows is the athlete's own account, not the agent's.

### In-flight rows

A row appears once the deliverable's payment has been requested or made. Work that is still in progress belongs to My Deals, not here.

On those rows `gross_amount` is the deliverable price and **`commission_percentage`, `agent_commission_amount`, and `net_amount` are all `null`**. So is `amount_to_agent`, when the agent is the recipient; an athlete-direct row reports `0` whether or not it has been paid, because the agent's share is already known to be nothing. This is deliberate: a figure calculated from the agent's current commission rate would read as a promise, and the rate that finally applies is whatever ends up stored on the payout. Show these as pending rather than as zero.

`payout_id` and `transaction_id` are also null until they exist.

<Note>
  Null and zero mean different things here. Null is "not settled yet"; zero is "settled, and none of it was the agent's".
</Note>

### Status values

| Status              | Meaning                                                                             |
| ------------------- | ----------------------------------------------------------------------------------- |
| `Payment Requested` | The athlete has requested payment for the deliverable                               |
| `Processing`        | The money is on its way, including a payout attempt that failed and will be retried |
| `Paid`              | The payout succeeded, to whoever `payout_recipient` names                           |

These are the three the athlete Pay screens use, and the feed carries no others. A deliverable's own lifecycle — assigned, submitted, approved — is My Deals' subject, and repeating it here made Pay read as a second deal list.

A failed payout reads `Processing` rather than disappearing or reading `Failed`: the money is still owed, and Pay has never had a failure state. Back Office is where a failed attempt is diagnosed.

Status describes the deal's payout, not the agent's share specifically. An athlete-direct row reads `Paid` once the athlete was paid, alongside `amount_to_agent: 0`.

A deliverable a brand settled outside MOGL is recorded rather than transferred, and it reads `Paid` with `payment_mode: "Paid Manually"`. On an agent-recipient deal the amount now shows as money to the agent, because the party the deal owed is the party the record names. Deals settled this way before this release name the athlete regardless, so they appear as athlete-direct; nothing observed who actually received that money, so it was not rewritten.

### What is not in this feed

<CardGroup cols={2}>
  <Card title="No deals the agent never represented" icon="ban">
    Membership comes from what the application recorded, not from the agent's current roster. An agent taking on an athlete does not inherit deals from before.
  </Card>

  <Card title="No subscriptions or card charges" icon="ban">
    Agent subscriptions are deprecated. There are no charges against an agent card, no billing-card last four, and no subscription rows.
  </Card>
</CardGroup>

Free product is **not** in the feed. A non-cash deliverable settles in product, so there is no payment to report, and the athlete Pay screens leave it out for the same reason. It stays visible in My Deals.

Voided deliverables are also absent. A voided deliverable is one nobody will be paid for.

### History survives unlinking

Rows are keyed to the representation the application recorded and to the payout that reached the agent, neither of which is deleted when an athlete is unlinked. An agent keeps seeing every deal they handled and what they were paid for it.

### Deals from before this feature

Applications made before representation was recorded were given it once, by a one-off migration, under rules that differ from the rules for new applications. Nothing about the response shape changes; only which old deals appear.

| Old deal                                         | Representation it was given           | Effect on Agent Pay                             |
| ------------------------------------------------ | ------------------------------------- | ----------------------------------------------- |
| Already paid, and the agent received the money   | That agent                            | Appears, with the real amounts                  |
| Already paid, and the athlete received the money | None                                  | Does not appear, even if the agent submitted it |
| Not yet paid                                     | The athlete's agent at migration time | Appears                                         |

The middle row is the one to expect questions about: a completed old deal that an agent submitted but the athlete was paid for is not in that agent's Pay view, because a finished payment is treated as the record of who was involved. Athlete-direct deals created after this release do carry representation and do appear, reading `amount_to_agent: 0`. The difference is confined to deals that were already paid before the migration ran.

## Empty and error states

| State | Guidance                                                                                                                                                                             |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Empty | Explain that Agent Pay shows the deals the agent represents and what MOGL paid them for that work. Where appropriate, link to My Payout Options for incomplete Stripe Express setup. |
| `403` | Not an agent account. Do not retry.                                                                                                                                                  |
| `401` | Token expired; re-authenticate.                                                                                                                                                      |

## Athlete notification

When a payout goes to an agent, the athlete's notification now says the money was sent to the named agent rather than that a payment is on its way to them. Clients do not need to change anything for this; it is server-side copy.

<Note>
  Email, push and SMS branch on the recipient server-side. The template rows for the agent variants are a content task, tracked separately from the client work.
</Note>

## Client checklist

<Steps>
  <Step title="Show agent involvement from is_agent_represented">
    Athlete My Deals and Pay, agent My Deals, iOS My Deals and Deal Details. Name them from `agent_name`.
  </Step>

  <Step title="Show anything about money from agent_gets_paid">
    Never from `is_agent_represented`, and never from who applied, and never from the current agent link.
  </Step>

  <Step title="Point the agent Pay route at /api/agent/pay-feed">
    Remove the athlete selector and any `selectedAthlete` dependency.
  </Step>

  <Step title="Render gross, retained, and net from the stored fields">
    Lead with `gross_amount` and `amount_to_agent`, and show `commission_amount` as the retained commission. Treat null amounts as pending and `0` as settled at nothing.
  </Step>

  <Step title="Keep failed payouts visible">
    They are part of the feed.
  </Step>
</Steps>

<Note>
  Swagger: `GET /agent/pay-feed` is documented in Core API (`Agent - Pay`).
</Note>
