Skip to content

Pay a reward from the campaign pool

POST
/open/v1/campaign-pool/payouts
curl --request POST \
--url https://api.harperharbor.com/open/v1/campaign-pool/payouts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "key": "creator-season-1-card-100001", "accountNo": 10001, "amount": 10000, "campaignRef": "creator-season-1", "contentRef": "card-123" }'

Credits a player’s account with amount credits taken from the campaign pool of the app this token belongs to. Only the app’s owners and admins can pay. accountNo is the player’s public account number. Reuse the same key when retrying; a key that already paid returns the original payout with replayed: true. Reusing a key with a different accountNo, amount, campaignRef, contentRef or validityDays returns idempotency_conflict. Usage scope comes from the platform’s settings for this app; expiry is chosen per payout with validityDays, and leaving it out means the credits never expire.

Media typeapplication/json
object
key
required

Your own payout identifier: 1 to 128 letters, digits, _ or -. A key pays at most once.

string
>= 1 characters <= 128 characters /^[A-Za-z0-9_-]{1,128}$/
accountNo
required

Public account number of the player to credit.

integer format: int64
>= 1
amount
required

Credits to pay. A positive integer not above maxPayout.

integer format: int64
>= 1
campaignRef

Optional campaign label for your own records, up to 128 characters.

string
<= 128 characters
contentRef

Optional reference to the content the reward is for, up to 128 characters.

string
<= 128 characters
validityDays

Optional. Days until the paid credits expire, from 1 to 3650. Leave it out for credits that never expire. Re-sending a key must carry the same value.

integer
>= 1 <= 3650
Example
{
"key": "creator-season-1-card-100001",
"accountNo": 10001,
"amount": 10000,
"campaignRef": "creator-season-1",
"contentRef": "card-123"
}

The payout. replayed is true when key had already paid.

Media typeapplication/json
object
key
required

The payout key.

string
accountNo
required

Public account number of the player who was credited.

integer format: int64
amount
required

Credits paid.

integer
entryId
required

Identifier of the pool entry that recorded the payout.

string format: uuid
paidAt
required

When the credits were paid.

string format: date-time
expiresAt
required

When the paid credits expire. null when they do not.

string | null format: date-time
usageScope
required

What the paid credits can be spent on: all or agent_only.

string
replayed
required

True when this response repeats an earlier payout of the same key; no new credits were paid.

boolean
Examplegenerated
{
"key": "example",
"accountNo": 1,
"amount": 1,
"entryId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"paidAt": "2026-04-15T12:00:00Z",
"expiresAt": "2026-04-15T12:00:00Z",
"usageScope": "example",
"replayed": true
}

invalid_request — key is not 1 to 128 letters, digits, _ or -; accountNo or amount is not a positive integer; or a reference is longer than 128 characters.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

unauthorized — missing or invalid bearer.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

forbidden — the caller is not an owner or admin of this app.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

account_not_found — no active account has this accountNo.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

pool_not_configured — the platform has not set the rules for this app yet; pool_disabled — payouts are paused for this app; payout_over_limit — amount is above the per-payout limit maxPayout; pool_insufficient — the pool cannot cover this payout, so retry with the same key after it is topped up; idempotency_conflict — this key was already used for a different recipient, amount or reference, so use a new key.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

temporarily_unavailable — retry later.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}