Skip to content

Activate a campaign

POST
/v1/game-instances/{campaignId}/activate
curl --request POST \
--url https://api.keliplay.com/v1/game-instances/example/activate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "handoverAt": "2026-11-01T00:00:00.000Z" }'

Makes a campaign live on its channel. The game config is checked first, and an incomplete one is refused (400).

The body is optional. To take over from a campaign that is already live on the channel, send handoverAt: the live campaign ends at that moment and this one starts then. Both changes happen together or not at all.

A channel runs one campaign at a time. If the live windows would overlap, the request is refused (409).

Permission: campaigns.activate. Sending handoverAt also needs channels.manage. Plan limit: number of live campaigns (402 when reached).

campaignId
required
string

The campaign’s id.

Media typeapplication/json
object
handoverAt

When this campaign takes over from the one live on the channel. Also becomes this campaign’s liveFrom.

string format: date-time
Example
{
"handoverAt": "2026-11-01T00:00:00.000Z"
}

The active campaign.

Media typeapplication/json
object
gameInstance
required
object
id
required
string
gameId
required
string
gameSlug
required
string
name
required
string
status
required
  • draft: being set up. Players cannot see it.
  • active: live on its channel, during its live window.
  • paused: hidden for now. Plays and results are kept.
  • archived: finished and put away. Can be restored.
string
Allowed values: draft active paused archived
channelId
required
string
config
required

The game’s own settings, such as wheel segments or quiz questions. The shape is different for each game. See the Games section for each game’s fields.

Each file a config names must be the right kind: a file under sounds a sound, any other an image. A file from your media library is checked by its type, any other address by its file extension. A mismatch is a 400 naming the field, such as config.art.background.

object
key
additional properties
any
prize
One of:

What the campaign is giving away, as shown to winners.

object
label
required
string
>= 1 characters
description
string
externalRef

Your own reference for the prize, such as a SKU.

string
theme
One of:

Colours, images, font, wording, background, position and decorations for your games.

Each file must be the kind its field takes: logoUrl and backgroundUrl an image, an animation or decoration the kind it names. A file from your media library is checked by its type, any other address by its file extension. A mismatch is a 400 naming the field.

object
colors

Six-digit hex colours, such as #3b8ad9.

object
bg
string
/^#[0-9a-fA-F]{6}$/
fg
string
/^#[0-9a-fA-F]{6}$/
muted
string
/^#[0-9a-fA-F]{6}$/
accent
string
/^#[0-9a-fA-F]{6}$/
danger
string
/^#[0-9a-fA-F]{6}$/
surface
string
/^#[0-9a-fA-F]{6}$/
border
string
/^#[0-9a-fA-F]{6}$/
logoUrl

An https URL, or a file from your media library.

string
<= 2048 characters
backgroundUrl

The background image, or an animation’s still. An https URL, or a file from your media library.

string
<= 2048 characters
fontFamily

A CSS font family list. Letters, digits, spaces, quotes, commas and hyphens.

string
>= 1 characters <= 120 characters /^[\w ,'"-]+$/
fontUrl

Where to load the font from.

string
<= 2048 characters
labels

Your own wording for player-facing text, keyed by label. Each game lists its label keys in labelKeys.

object
<= 40 properties
key
additional properties
string
<= 200 characters
layout

Where the game sits when it fills a phone’s screen. Has no effect inside a website’s frame.

object
position

Defaults to center.

string
Allowed values: top center bottom
width

game keeps each game’s own width, wide allows at least 560 pixels, full uses the whole screen.

string
Allowed values: game wide full
background

How the background is painted, on the pre-game page, the game and the reward page.

object
fit

How backgroundUrl fills the screen. Defaults to cover.

string
Allowed values: cover contain tile
focus

The part of the image kept in view when it is cropped. Defaults to center.

string
Allowed values: top-left top top-right left center right bottom-left bottom bottom-right
gradient

Painted in place of the flat colors.bg.

object
from
required
string
/^#[0-9a-fA-F]{6}$/
to
required
string
/^#[0-9a-fA-F]{6}$/
angle
required
integer
<= 359
overlay

A tint over the background, for legibility.

object
color
required
string
/^#[0-9a-fA-F]{6}$/
opacity
required

Percent.

integer
<= 80
animation

Moving art from your media library. backgroundUrl is shown first, and instead when motion is off, data is being saved, or it cannot play. Videos play muted, only when the game fills a phone’s screen.

object
kind
required
string
Allowed values: lottie video
url
required
string
<= 2048 characters
decorations

Artwork along the top and bottom edges. It never covers the game.

object
top
object
url
required

An https URL, or a file from your media library. An image repeats along its edge.

string
<= 2048 characters
kind
required
string
Allowed values: image lottie
size
required

The height of its band, 48, 96 or 160 pixels.

string
Allowed values: small medium large
bottom
object
url
required

An https URL, or a file from your media library. An image repeats along its edge.

string
<= 2048 characters
kind
required
string
Allowed values: image lottie
size
required

The height of its band, 48, 96 or 160 pixels.

string
Allowed values: small medium large
grantOncePerParticipant

Whether a player can receive at most one coupon from this campaign. Set it with the reward.

boolean
rewardValidForSeconds

How long a granted coupon stays valid. Set it with the reward.

integer | null
winnerSelectionTrigger
required

When winners are drawn.

  • manual: when you call the draw operation.
  • auto_on_session_end: after each play ends.
  • auto_on_schedule: once, at scheduleAt.
string
default: manual
Allowed values: manual auto_on_session_end auto_on_schedule
winnerSelectionAlgorithm
required

How winners are picked from eligible entries. Defaults to the game’s defaultWinnerAlgorithm.

  • random_uniform: every entry has the same chance.
  • random_weighted: chance follows each entry’s weight.
  • score_highest: highest scores win.
string
Allowed values: random_uniform random_weighted score_highest
winnerSelectionConfig
required
object
maxWinners
integer
>= 1 <= 1000
tieBreak

How equal scores are ordered, for score_highest.

string
Allowed values: earliest_entry random
windowStart

Only entries made at or after this time count.

string format: date-time
windowEnd

Only entries made before this time count.

string format: date-time
scheduleAt

When an auto_on_schedule draw runs.

string | null format: date-time
resolvedOptionId

The option that happened, on a prediction campaign.

string | null
resolvedAt
string | null format: date-time
maxPlaysPerParticipant
integer | null
maxPlaysPerParticipantPerDay
integer | null
playLimitTimezone
required
string
liveFrom
string | null format: date-time
liveUntil
string | null format: date-time
activatedAt

When the campaign was first activated.

string | null format: date-time
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
Examples
Exampleactivated

A campaign response

{
"gameInstance": {
"id": "0192f3a1-0000-7000-8000-0000000ca001",
"gameId": "0192f3a1-0000-7000-8000-0000000a9001",
"gameSlug": "spin-wheel",
"name": "Autumn wheel",
"status": "active",
"channelId": "0192f3a1-0000-7000-8000-00000000c001",
"config": {
"segments": [
{
"id": "segment-1",
"label": "Free coffee",
"weight": 1
},
{
"id": "segment-2",
"label": "Try again",
"weight": 4
}
]
},
"prize": {
"label": "Free coffee",
"description": "One regular coffee at any store."
},
"theme": null,
"grantOncePerParticipant": true,
"rewardValidForSeconds": 2592000,
"winnerSelectionTrigger": "manual",
"winnerSelectionAlgorithm": "random_weighted",
"winnerSelectionConfig": {
"maxWinners": 3
},
"scheduleAt": null,
"resolvedOptionId": null,
"resolvedAt": null,
"maxPlaysPerParticipant": null,
"maxPlaysPerParticipantPerDay": 1,
"playLimitTimezone": "Europe/London",
"liveFrom": "2026-10-01T00:00:00.000Z",
"liveUntil": "2026-10-31T23:59:59.000Z",
"activatedAt": "2026-09-30T12:00:00.000Z",
"createdAt": "2026-09-25T09:00:00.000Z",
"updatedAt": "2026-09-30T12:00:00.000Z"
}
}

The request is not valid. details lists each field that failed, where there is one.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "invalid_request",
"message": "Invalid request json.",
"details": [
{
"path": "name",
"message": "Too small, expected string to have >=1 characters"
}
]
}
}

The API key is missing, malformed or revoked.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
}
}

Your plan does not include this feature, or you have reached a plan limit.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "upgrade_required",
"message": "Rewards is not included in your plan. Ask whoever manages your account to add it."
}
}

The API key does not carry the permission this needs, or the request is not allowed.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "forbidden",
"message": "Your role does not allow this (draws.run)."
}
}

Nothing with that id exists in your account.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "not_found",
"message": "Channel 0192f3a1-0000-7000-8000-00000000c009 not found."
}
}

The request clashes with the current state. The message says what to change.

Media typeapplication/json
object
error
required
object
code
required

Stable and safe to branch on.

  • invalid_request (400): the request is not valid.
  • unauthorized (401): missing, malformed or revoked API key.
  • upgrade_required (402): not in your plan, or a plan limit is reached.
  • forbidden (403): the key lacks a permission, or the action is not allowed.
  • not_found (404): nothing with that id in your account.
  • conflict (409): clashes with the current state.
  • confirmation_required (409): repeat with confirm=true. Folder deletion only.
  • rate_limited (429): wait for Retry-After seconds.
  • internal_error (500) and service_unavailable (503): try again later.
string
Allowed values: invalid_request unauthorized upgrade_required forbidden not_found conflict confirmation_required rate_limited internal_error service_unavailable
message
required

Written for a person. May change; do not parse it.

string
details
One of:
Array<object>
object
path
required

The failing field, dot separated, such as prize.label.

string
message
required
string
Example
{
"error": {
"code": "conflict",
"message": "An archived campaign cannot be edited. Restore it first, then make the change."
}
}