Skip to main content
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

No agent

The athlete was not represented when they applied. MOGL pays the athlete, and no agent is involved in the deal at all.

Represented, agent paid

MOGL pays the agent through Stripe Express, after retaining the agent commission. The agent settles with the athlete outside MOGL.

Represented, athlete paid

The Notre Dame arrangement. MOGL pays the athlete. The athlete settles with the agent outside MOGL.
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.
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.

Where the flags appear

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.

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

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.

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

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

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

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.

Reading the money fields

This is the part most likely to be displayed wrongly, so it is worth being precise. 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.
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.

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.
Null and zero mean different things here. Null is “not settled yet”; zero is “settled, and none of it was the agent’s”.

Status values

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

No deals the agent never represented

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.

No subscriptions or card charges

Agent subscriptions are deprecated. There are no charges against an agent card, no billing-card last four, and no subscription rows.
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. 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

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

Client checklist

1

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

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

Point the agent Pay route at /api/agent/pay-feed

Remove the athlete selector and any selectedAthlete dependency.
4

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

Keep failed payouts visible

They are part of the feed.
Swagger: GET /agent/pay-feed is documented in Core API (Agent - Pay).