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

# Approve Campaign Submission

> Approve a pending campaign submission and create its earning

Approve a `PENDING` submission. For non-retainer campaigns, approval creates one `CAMPAIGN_REWARD` earning in `READY_FOR_PAYMENT` and queues the partner approval email. Requires `write:affiliates`. Foreign-team IDs return 404.

The same operation is available through the [`affonso_approve_campaign_submission`](https://docs.affonso.io/mcp/tools/campaigns) MCP tool.

## Path Parameters

**id** `string` *required*

Encoded campaign id

**submissionId** `string` *required*

Encoded submission id. No public REST endpoint, MCP tool, or webhook lists campaign submissions or returns their ids, so get it from the submission in Affonso. This endpoint returns it as `id`.

## Body Parameters

The body is optional. Unknown body fields are rejected.

**amount** `number`

Reward amount. Required for `custom` and `range` campaigns. Minimum 0.01, maximum 999,999.99. The amount is not checked against the published `reward_amount_min` / `reward_amount_max` of a `range` campaign. Ignored for `flat_rate` (the campaign's `reward_amount` is used) and `retainer` campaigns.

**reject\_open\_applications** `boolean`

Only applies when this approval reaches the campaign's `max_budget` and closes the campaign. When `true`, all `PENDING` applications are rejected. When omitted or `false`, they stay open.

## Response

Returns the approved submission object.

**success** `boolean`

Always `true` for successful responses

**data** `object`

The submission.

**Data Object Properties**

**id** `string`

Encoded submission id

**campaign\_id** `string`

Encoded campaign id

**affiliate\_id** `string`

Affiliate id

**status** `string`

Submission status. After approval this is `"APPROVED"`. Valid values: `PENDING`, `APPROVED`, `REJECTED`.

**custom\_reward\_amount** `number | null`

Reward amount written to the submission on approval. Not written for retainer submissions.

**earning\_id** `string | null`

Id of the earning created by this approval. `null` for retainer submissions, which create no earning, and for approvals made before earnings were created on approval.

**earning\_status** `string | null`

Current status of the earning. A newly created earning is `"READY_FOR_PAYMENT"`. `null` when `earning_id` is `null`.

**reviewed\_at** `string | null`

ISO 8601 review timestamp

**created\_at** `string`

ISO 8601 creation timestamp

**updated\_at** `string`

ISO 8601 update timestamp

**campaign\_closed\_by\_budget** `boolean`

`true` when this approval reached or exceeded the campaign's `max_budget` and moved the campaign to `ENDED`

**rejected\_pending\_applications** `integer`

Number of pending applications rejected by this call. Non-zero only when `reject_open_applications` is `true` and the campaign was closed by budget.

```bash cURL (custom or range campaign) theme={null}
curl -X POST "https://api.affonso.io/v1/program/campaigns/cmcustom01camp0001/submissions/cmsub01custom00001/approve" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 250
  }'
```

```bash cURL (flat rate campaign) theme={null}
curl -X POST "https://api.affonso.io/v1/program/campaigns/cmlaunch01camp0001/submissions/cmsub01launch00001/approve" \
  -H "Authorization: Bearer sk_live_your_api_key"
```

```json Response (flat rate) theme={null}
{
  "success": true,
  "data": {
    "id": "cmsub01launch00001",
    "campaign_id": "cmlaunch01camp0001",
    "affiliate_id": "aff_abc123",
    "status": "APPROVED",
    "custom_reward_amount": 100,
    "earning_id": "cmearn01launch0001",
    "earning_status": "READY_FOR_PAYMENT",
    "reviewed_at": "2026-10-07T12:00:00.000Z",
    "created_at": "2026-10-06T15:20:00.000Z",
    "updated_at": "2026-10-07T12:00:00.000Z",
    "campaign_closed_by_budget": false,
    "rejected_pending_applications": 0
  }
}
```

## Earning

For `flat_rate`, `custom`, and `range` campaigns, approval creates one earning in the same transaction:

* `earning_type` is `CAMPAIGN_REWARD`, with status `READY_FOR_PAYMENT` and no hold period.
* The amount is the approved reward amount, in your team's currency.
* The earning's description is the campaign name.

Retainer proof submissions create no earning. Retainer earnings are created per period and approved separately with [Approve Retainer Period](https://docs.affonso.io/api/endpoint/campaigns/approve-retainer-period.md).

## Partner email

When approval creates an earning, the partner approval email is queued in the same transaction. No email is queued when the `campaign-submission-approved` [email notification](https://docs.affonso.io/api/endpoint/program/list-notifications.md) is disabled, when the program has no portal settings, or when the affiliate has no valid email address. None of these affect earning creation.

## Max budget

If the approved amount brings the campaign's approved reward spend to or above its owner `max_budget`, the approval still succeeds. A campaign with `status` `ACTIVE` or `SCHEDULED` is then moved to `ENDED` and `campaign_closed_by_budget` is `true`. Submissions of an `ENDED` campaign can still be approved.

## Notes

* Requires `write:affiliates`.
* There is no public endpoint for listing submissions. The [`affonso_approve_campaign_submission`](https://docs.affonso.io/mcp/tools/campaigns) MCP tool has the same limitation.
* Approving an already `APPROVED` submission is idempotent (HTTP 200). It returns the existing submission with its `earning_id` and `earning_status`, creates no new earning or email, and reports `campaign_closed_by_budget` as `false` and `rejected_pending_applications` as `0`.
* A `REJECTED` submission returns `CONFLICT` (HTTP 409). If a concurrent call reviews the submission first, the call may also return `CONFLICT` with "This submission was already reviewed".
* `custom` and `range` campaigns without an `amount` return `VALIDATION_ERROR` (HTTP 400) with `field` `amount`. An `amount` below 0.01 or above 999,999.99 fails body validation and returns `VALIDATION_ERROR` (HTTP 400) with `details`, before the submission is claimed, so no earning is created.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.