Draw winners
const url = 'https://api.keliplay.com/v1/game-instances/example/winner-selection/trigger';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"idempotencyKey":"draw-2026-10-31-final","maxWinners":3}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.keliplay.com/v1/game-instances/example/winner-selection/trigger \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "idempotencyKey": "draw-2026-10-31-final", "maxWinners": 3 }'Draws winners from the campaign’s eligible entries now. Every draw is recorded and can be verified.
Only campaigns whose winnerSelectionTrigger is manual can be drawn
on demand (409 otherwise). The campaign must be active.
idempotencyKey is required. Repeating a request with the same key
returns the first draw with replayed: true and status 200 rather than
drawing again, so retries are safe.
If the campaign’s prizes are given by draw, winners receive their
coupons as part of the draw. grants lists which winners received one.
Read the codes with GET .../reward-grants.
Permission: draws.run. Rate limit: 30 requests per minute.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The campaign’s id.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Your own unique key for this draw.
How many winners to draw. Defaults to the campaign’s winnerSelectionConfig.maxWinners.
Example
{ "idempotencyKey": "draw-2026-10-31-final", "maxWinners": 3}Responses
Section titled “Responses”A repeat of an earlier request with the same idempotencyKey. Nothing new was drawn.
object
One draw. Every draw is recorded and can be verified.
object
When winners are drawn.
manual: when you call the draw operation.auto_on_session_end: after each play ends.auto_on_schedule: once, atscheduleAt.
The API key that triggered it, if one did.
The algorithm used, as in winnerSelectionAlgorithm.
object
How equal scores are ordered, for score_highest.
Only entries made at or after this time count.
Only entries made before this time count.
How many eligible entries the draw picked from.
object
True when this repeats an earlier draw with the same idempotencyKey.
Winners who received a coupon in this draw. Codes are not included.
object
Example
{ "run": { "triggeredBy": "manual", "algorithmConfigSnapshot": { "tieBreak": "earliest_entry" }, "status": "pending" }}Headers
Section titled “Headers”Requests allowed in the current window.
Requests left in the current window.
A new draw.
object
One draw. Every draw is recorded and can be verified.
object
When winners are drawn.
manual: when you call the draw operation.auto_on_session_end: after each play ends.auto_on_schedule: once, atscheduleAt.
The API key that triggered it, if one did.
The algorithm used, as in winnerSelectionAlgorithm.
object
How equal scores are ordered, for score_highest.
Only entries made at or after this time count.
Only entries made before this time count.
How many eligible entries the draw picked from.
object
True when this repeats an earlier draw with the same idempotencyKey.
Winners who received a coupon in this draw. Codes are not included.
object
Examples
A new draw
{ "run": { "id": "0192f3a1-0000-7000-8000-0000000d0001", "gameInstanceId": "0192f3a1-0000-7000-8000-0000000ca001", "triggeredBy": "manual", "triggeredByApiKeyId": "0192f3a1-0000-7000-8000-0000000ce001", "idempotencyKey": "draw-2026-10-31-final", "algorithm": "random_uniform", "algorithmConfigSnapshot": { "maxWinners": 1 }, "entriesSnapshotCount": 412, "status": "completed", "failureReason": null, "executedAt": "2026-10-31T18:00:01.000Z", "createdAt": "2026-10-31T18:00:00.000Z" }, "winners": [ { "id": "0192f3a1-0000-7000-8000-0000000e0001", "runId": "0192f3a1-0000-7000-8000-0000000d0001", "gameInstanceId": "0192f3a1-0000-7000-8000-0000000ca001", "entryId": "0192f3a1-0000-7000-8000-0000000f0001", "participantRef": "user_48213", "rank": 1, "prize": { "label": "Free coffee" }, "notifiedAt": null, "createdAt": "2026-10-31T18:00:01.000Z" } ], "replayed": false, "grants": [ { "winnerId": "0192f3a1-0000-7000-8000-0000000e0001", "grantId": "0192f3a1-0000-7000-8000-000000073003" } ]}Headers
Section titled “Headers”Requests allowed in the current window.
Requests left in the current window.
The request is not valid. details lists each field that failed, where there is one.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
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.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
Example
{ "error": { "code": "unauthorized", "message": "Invalid or revoked API key." }}The API key does not carry the permission this needs, or the request is not allowed.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
Example
{ "error": { "code": "forbidden", "message": "Your role does not allow this (draws.run)." }}Nothing with that id exists in your account.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
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.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
Example
{ "error": { "code": "conflict", "message": "An archived campaign cannot be edited. Restore it first, then make the change." }}Rate limit exceeded. Wait for Retry-After seconds.
object
object
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 withconfirm=true. Folder deletion only.rate_limited(429): wait forRetry-Afterseconds.internal_error(500) andservice_unavailable(503): try again later.
Written for a person. May change; do not parse it.
object
The failing field, dot separated, such as prize.label.
object
Example
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded: 30 requests per 60s." }}Headers
Section titled “Headers”Seconds to wait before trying again.