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

# Campaign quick pay

> Send or submit a campaign payment for approval, with custom line items, fee allocation, and safe retries.

## POST /sdk/v1/campaign-creators/:campaignCreatorRecordId/quick-pay

Send a one-off payment to an existing campaign creator, using the same direct
payout flow as the campaign screen. You supply the line items and amounts.
Grade records the payment as `DIRECT` against that creator and campaign,
includes it in campaign spend and payout history, and creates the invoices.

Quick pay does **not** settle calculated fixed fees, CPM earnings, or tiered
bonuses, and does not change the creator's contract. Those earnings remain
payable through [calculated campaign payouts](/campaign-creators/send-payouts).
For a payment without campaign attribution, use [Payouts](/payouts).

<Warning>
  When agency approvals are enabled, Quick Pay always requires approval,
  including requests from admins and finance users in the campaign screen.
  Otherwise, it initiates payment immediately unless you set `requireApproval: true`.
  Keep keys with `payouts:write` on your server.
</Warning>

## Prerequisites

* An SDK application and API key with `payouts:write`. Use `campaigns:read` to
  discover records and `payouts:read` to check payment status and invoices.
* An existing, active campaign creator record in your agency. Find its
  `campaignCreatorRecordId` through the [creator list](/campaign-creators/list).
  A creator profile ID or email cannot replace this record ID.
* An eligible creator and usable wallets. Sufficient funds are required when
  payment executes, under your agency's settlement settings; submitting an approval
  does not reserve or debit funds. Removed or deleted records, staged creators,
  and test phase creators when that feature is enabled cannot be paid.

## Headers

| Header | Required | Meaning |
| - | - | - |
| `X-Application-Id` | Yes | Your SDK application ID. |
| `X-Api-Key` | Yes | An API key with `payouts:write`. |
| `X-Idempotency-Key` | Yes | A unique key for this intended payment, 1–100 characters after trimming. Persist it before sending the request. |
| `Content-Type` | Yes | `application/json`. |

The authenticated application determines the agency. Do not send an `agencyId`,
`campaignId`, recipient email, or creator profile ID in the body.

## Request body

| Field | Type | Required | Meaning |
| - | - | - | - |
| `currency` | string | Yes | `USD`, `EUR`, `GBP`, or `CAD`, in uppercase. Applies to every line item. |
| `lineItems` | array | Yes | 1–100 custom line items. The gross payout is their sum. |
| `lineItems[].amount` | number | Yes | Positive amount with at most two decimal places. Send a JSON number, not a numeric string. |
| `lineItems[].description` | string | No | Up to 255 characters after trimming. Omitted or blank descriptions become `Direct payout`. |
| `requireApproval` | boolean | No | Defaults to `false`. Set to `true` to queue for approval even when agency approvals are disabled. `false` cannot bypass agency approvals. |
| `confirmHighValuePayout` | boolean | No | Defaults to `false`. Set to `true` to acknowledge a payout above your agency's threshold. |
| `platformFeePayerOverride` | object | No | Override who pays the platform fee for this payment only. Omit to inherit configured defaults. |

Unknown fields are rejected, including fields inside line items and fee overrides.
Amounts and their sum must fit within JavaScript's safe integer range when
expressed in cents. Sub-cent amounts are rejected rather than rounded.

### Platform fees

`platformFeePayerOverride` accepts one of these objects:

```json theme={null}
{ "mode": "INHERIT" }
```

```json theme={null}
{ "mode": "CREATOR_PAYS_ALL" }
```

```json theme={null}
{ "mode": "AGENCY_PAYS_ALL" }
```

```json theme={null}
{ "mode": "CUSTOM_SPLIT", "agencySharePercent": 40 }
```

For `CUSTOM_SPLIT`, `agencySharePercent` is required and must be between 0 and
100 inclusive. Other modes do not accept it. This changes fee allocation, not
the configured fee rate or cap. The creator's net receipt can be lower than the
gross line-item sum; the agency's debit can be higher when it pays fees.

### Currency conversion and confirmation

Grade converts the requested amount to the creator's existing wallet currency
when needed. A new creator wallet uses the requested currency. The response
includes both the requested gross total and its creator-currency equivalent.
Neither amount is a guarantee of the final net receipt after fees.

The high-value threshold is evaluated in your agency's preferred currency. If
confirmation is required, the API returns `400` with
`code: "THRESHOLD_CONFIRMATION_REQUIRED"`, `requiresThresholdConfirmation: true`,
`thresholdAmount`, and `thresholdCurrency`. Review the amount, then send
`confirmHighValuePayout: true` with a **new idempotency key**, because the request
body has changed. Confirmation does not bypass eligibility or balance checks.

## Example request

`BASE_URL` is the API origin and prefix before `/sdk/v1`, for example
`https://infra-sandbox.usegrade.com/api` for sandbox or
`https://infra.usegrade.com/api/email` for production. Use credentials issued for
the selected environment.

```bash theme={null}
curl -sS "$BASE_URL/sdk/v1/campaign-creators/CCR_123/quick-pay" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-Application-Id: $APP_ID" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Idempotency-Key: event-appearance-2026-10-03-001" \
  -d '{
    "currency": "USD",
    "lineItems": [
      { "description": "Event appearance fee", "amount": 150 },
      { "description": "Travel reimbursement", "amount": 75.5 }
    ],
    "platformFeePayerOverride": { "mode": "AGENCY_PAYS_ALL" }
  }'
```

### JavaScript integration

Persist your payment record, body, and idempotency key together before calling
the API. Pass that same saved record to this function again after a timeout.

```javascript theme={null}
async function quickPayCampaignCreator({ recordId, idempotencyKey, body }) {
  const response = await fetch(
    `${process.env.GRADE_BASE_URL}/sdk/v1/campaign-creators/${encodeURIComponent(recordId)}/quick-pay`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Application-Id": process.env.GRADE_APP_ID,
        "X-Api-Key": process.env.GRADE_API_KEY,
        "X-Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(body),
    },
  );
  const result = await response.json();
  if (!response.ok) {
    const error = new Error(result.message || "Quick pay failed");
    error.status = response.status;
    error.code = result.code;
    throw error;
  }
  return result;
}
```

## Immediate payment response — HTTP 200

```json theme={null}
{
  "success": true,
  "payoutId": "CCPAY_123",
  "invoiceId": "INV_CREATOR_123",
  "agencyInvoiceId": "INV_AGENCY_123",
  "transactionId": "TXN_123",
  "campaignId": "CAMP_123",
  "campaignCreatorRecordId": "CCR_123",
  "amount": 225.5,
  "currency": "USD",
  "creatorAmount": 225.5,
  "creatorCurrency": "USD",
  "lineItems": [
    { "description": "Event appearance fee", "amount": 150 },
    { "description": "Travel reimbursement", "amount": 75.5 }
  ]
}
```

* `amount` and `currency`: the gross sum in the currency you requested.
* `creatorAmount` and `creatorCurrency`: the gross amount in the creator's wallet currency.
* `lineItems`: normalized descriptions and amounts in the requested currency.
* `payoutId`: use with [payout status and breakdowns](/payouts).
* `invoiceId` and `agencyInvoiceId`: creator and agency invoice IDs for
  [PDF downloads](/invoices).
* `transactionId`: the agency payment transaction.

`200` means the payment was initiated, or the original response was replayed.
It does **not** mean the creator has received or withdrawn the funds. Check
`POST /sdk/v1/payouts/status` using the same application and a key with
`payouts:read`. Campaign quick pay creates no `pending_payout_id`.

## Require approval — HTTP 202

Add `"requireApproval": true` to the same request body to request review:

```json theme={null}
{
  "currency": "USD",
  "requireApproval": true,
  "lineItems": [{ "description": "Event appearance fee", "amount": 150 }]
}
```

| Agency approvals | `requireApproval` | Result |
| - | - | - |
| Off | Omitted or `false` | `200`: payment initiated. |
| Off | `true` | `202`: queued for approval. |
| On | Any value or omitted | `202`: queued for approval. |

A queued request creates invoices in `PENDING_APPROVAL` and a request in the
Grade **Approvals** queue. No payment transaction or wallet debit occurs at
submission. Admins or finance users with access to the campaign can review it
there, including when agency approvals are disabled. The submitter is displayed
as **API application**. Approval uses the existing permissions and review workflow.

```json theme={null}
{
  "success": true,
  "status": "PENDING",
  "approvalId": "cd098819-cb2c-442b-a44b-80bd1de6e0e3",
  "invoiceId": "INV_CREATOR_123",
  "agencyInvoiceId": "INV_AGENCY_123",
  "campaignId": "CAMP_123",
  "campaignCreatorRecordId": "CCR_123",
  "amount": 150,
  "currency": "USD",
  "creatorAmount": 150,
  "creatorCurrency": "USD",
  "lineItems": [{ "description": "Event appearance fee", "amount": 150 }]
}
```

`202` means **submitted for approval**, not paid. There is no `payoutId` or
`transactionId` yet. Both `200` and `202` are successful HTTP responses; check
for `approvalId` before attempting to track a payout. High-value confirmation
is still required when applicable and does not replace approval.

Requested line items and fee allocation are frozen for review. USD settlement
amounts are repriced at approval time using the current exchange rate. Wallet,
funding, campaign, and creator checks still apply at execution. Rejected or
cancelled requests do not execute a payment. To submit a replacement, use a new
idempotency key. Changing the agency approval setting does not approve an
existing pending request.

### GET /sdk/v1/campaign-creators/:campaignCreatorRecordId/quick-pay/approvals/:approvalId

Read the current approval state using the **same application** that submitted it
and an API key with `payouts:read`. This endpoint needs no idempotency key. It
returns `404` if the approval, agency, application, or creator record does not
match. A different application in the same agency cannot read the request.

```bash theme={null}
curl -sS "$BASE_URL/sdk/v1/campaign-creators/CCR_123/quick-pay/approvals/$APPROVAL_ID" \
  -H "X-Application-Id: $APP_ID" \
  -H "X-Api-Key: $API_KEY"
```

```json theme={null}
{
  "success": true,
  "approvalId": "cd098819-cb2c-442b-a44b-80bd1de6e0e3",
  "status": "APPROVED",
  "campaignId": "CAMP_123",
  "campaignCreatorRecordId": "CCR_123",
  "invoiceId": "INV_CREATOR_123",
  "agencyInvoiceId": "INV_AGENCY_123",
  "payoutId": "CCPAY_123",
  "rejectionNote": null,
  "createdAt": "2026-10-05T09:00:00.000Z",
  "decidedAt": "2026-10-05T10:00:00.000Z"
}
```

| Status | Meaning |
| - | - |
| `PENDING` | Waiting for review; no debit. |
| `APPROVING` | Execution is in progress; poll with backoff. |
| `APPROVED` | Payment initiated. Use `payoutId` with [payout status](/payouts) to track delivery. |
| `REJECTED` | Review declined; `rejectionNote` contains the reason when supplied. |
| `CANCELLED` | Request cancelled; no payout from this request. |

`payoutId` and `decidedAt` are `null` until applicable; `rejectionNote` is nullable.
After approval, the originating application can access payout status, breakdowns,
and invoice PDFs using its existing `payouts:read` permission. Pending invoice
IDs identify the frozen approval invoices. Invoice PDF access follows the
existing agency access rules. The status endpoint returns `401` for invalid
credentials, `403` for missing read permission, and `500` for a temporary read
failure. Retry read failures with backoff.

## Safe retries

Keys are scoped to your application and the campaign quick pay operation.
The URL record ID and normalized request body are part of the request identity.
Reusing a key for another creator or changed amounts returns `409`.

Successful payment or approval submission and replay data commit together.
Replaying a queued submission always returns its original `202` response, even
after review. Use the GET approval endpoint for its current state. Completed responses have
no time-based expiry. If the network times out or the server returns `500`,
retry with the **same record ID, body, and key**. Do not generate a fresh key to
recover an uncertain payment. A request still running returns `409`; retry it
with backoff. An interrupted worker's processing lease becomes recoverable
after five minutes.

Business failures after processing starts, such as insufficient funds or a
required high-value confirmation, are also replayed. After resolving a known
failure, use a new key for the new payment attempt. Authentication and input
validation failures happen before the key is claimed.

## Errors

Errors return `{ "success": false, "code": "...", "message": "..." }`, except
authentication and scope errors, which may omit `code`.

| HTTP | Code | Meaning and next step |
| - | - | - |
| `400` | `INVALID_IDEMPOTENCY_KEY`, `INVALID_REQUEST` | Correct the key or request fields. No payment started. |
| `400` | `CREATOR_NOT_PAYABLE`, `TEST_PHASE_CREATOR` | Resolve the creator's eligibility before a new attempt. |
| `400` | `PAYOUT_PREPARATION_FAILED`, `CURRENCY_CONVERSION_FAILED` | The amount could not be prepared or the threshold could not be evaluated. |
| `400` | `THRESHOLD_CONFIRMATION_REQUIRED` | Review, then explicitly confirm with a new key. |
| `400` | `APPROVAL_SUBMISSION_FAILED` | Invoice or approval validation failed; no payment started. |
| `400` | `PAYOUT_FAILED` | Wallet, balance, fee, or payment validation failed. `insufficientFundsDetails` may include available and required USD amounts. |
| `401` | — | Missing or invalid credentials. |
| `403` | — | Missing `payouts:write`, SDK access disabled, or origin disallowed. |
| `404` | `NOT_FOUND` | No active campaign creator in your agency matches the record ID. |
| `409` | `IDEMPOTENCY_PAYLOAD_MISMATCH` | This key belongs to a different request. |
| `409` | `IDEMPOTENCY_IN_PROGRESS`, `PAYOUT_IN_PROGRESS` | Retry the original request and key with backoff. |
| `500` | `INTERNAL_ERROR` | Outcome may be uncertain; retry the original request and key. |
| `503` | `IDEMPOTENCY_UNAVAILABLE` | Protection unavailable; retry the original request and key. |


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