# Payout Receiver
## Summary
ACP currently treats the provider as both the operational actor and the payment recipient. This document discusses separating those roles so a job can have:
```text
provider = actor authorized to perform job actions
payoutReceiver = address that receives provider-side payments
```
If no separate receiver is configured, `payoutReceiver` should default to `provider`.
The goal is not to standardize one fee model. The goal is to give ACP a minimal payout extension point so integrations can choose whether fees are handled natively by ACP or downstream by a receiver contract.
## Why This Matters
### 1. Wallet Segregation
Some integrations need separate wallets for operations and fund management.
The provider wallet may be an agent, operator, hot wallet, or automation account that submits work and calls `settle`. The payout receiver may be a treasury, multisig, smart account, accounting wallet, or payment router.
This keeps operational authority separate from fund custody.
### 2. Custom Fee Disbursement
Some integrations may want fee logic outside ACP.
In this model:
```text
ACP platform fee = 0
ACP evaluator fee = 0
ACP pays provider net amount to payoutReceiver
payoutReceiver performs custom downstream disbursement
```
The receiver contract can then split funds across provider treasury, evaluator, platform, affiliates, or other parties according to its own rules.
The selected fee and disbursement policy should be explicit to clients before they authorize settlement or completion.
### 3. Cleaner Than Hook-Based Disbursement
Hook-based payment splitting can require `approve` and `transferFrom`, which adds allowance state, extra approvals, and payment logic outside the core escrow flow.
A payout receiver is simpler: ACP sends funds once to the configured receiver, then the receiver splits funds it already controls.
This keeps ACP flexible: it can either apply native platform and evaluator fees before paying the receiver, or set ACP-level fees to `0` and let the receiver contract own the downstream routing policy.
## Sequence
```mermaid
sequenceDiagram
participant C as Client
participant AC as ACP
participant P as Provider
participant E as Evaluator
participant R as PayoutReceiver / IDisburser
C->>AC: createJob(provider, ..., payoutReceiver)
P->>AC: setBudget(jobId, token, amount, "0x")
C->>AC: fund(jobId, amount, "0x")
P->>AC: submit(jobId, deliverable, "0x")
E->>AC: complete(jobId, reason, data)
Note over AC: compute provider net amount
AC->>R: transfer token amount
AC->>R: onDisbursement(jobId, complete.selector, token, amount, data)
Note over R: custom accounting / fee split
R->>E: disburse evaluator fee
R->>P: disburse provider job reward
```
If `payoutReceiver == address(0)`, ACP pays the provider directly and does not call `onDisbursement`.
## Expected ACP Behavior
Provider authorization should not change:
- only the provider can submit work
- only the provider can call `settle`
- evaluator approval rules remain unchanged
Payment disbursement changes:
- `settle` pays the provider net amount to `payoutReceiver`
- `complete` pays the provider net remainder to `payoutReceiver`
- refunds still go to the client
- platform and evaluator fee recipients are unchanged when ACP fees are enabled
## Payout Address
The user specifies an optional payout receiver address:
```text
payoutReceiver
```
If `payoutReceiver == address(0)`, ACP pays the provider.
If `payoutReceiver != address(0)`, ACP pays the payout receiver.
## Receiver Callback
If a payout receiver is specified, ACP should notify the receiver after provider-side funds are transferred.
The receiver should implement:
```solidity
interface IDisburser {
function onDisbursement(
uint256 jobId,
bytes4 selector,
address token,
uint256 amount,
bytes calldata data
) external;
}
```
Parameters:
- `jobId` identifies the job. This is sufficient for the receiver to query ACP for job details.
- `selector` identifies the ACP function that triggered the disbursement, such as `settle` or `complete`.
- `token` identifies the payment token transferred to the receiver.
- `amount` is the provider-side net amount transferred to the receiver.
- `data` provides arbitrary additional information for receiver-side processing.
Callback rule:
- if `payoutReceiver == address(0)`, ACP pays the provider and does not call `onDisbursement`
- if `payoutReceiver != address(0)`, ACP pays the receiver and calls `onDisbursement`
This lets a receiver contract trigger custom accounting, fee splits, routing, or downstream settlement after it receives funds.
## Why `IDisburser`
A receiver contract could also be implemented as the job hook, but that couples lifecycle gating with payout processing. Keeping `IDisburser` separate gives each extension point a clearer responsibility: hooks gate or observe job actions, while the payout receiver handles funds after disbursement.