Automation Scripts

A script is one of the two kinds of automation action. When a rule fires, its script runs once, with information about the event that triggered it. Scripts are best for fixed, predictable steps — award an achievement, move a member into a group, set a medal — that should run the same way every time.

The language

Scripts are written in Starlark, a small, deterministic dialect of Python. If you know a little Python, it will look familiar: variables, if/elif/else, for loops, lists, dictionaries, and function calls all work the same way. For the full language, see the Starlark specification.

Starlark is intentionally limited. A script cannot read files, make network requests, or import packages — it can only look at the event and call the functions listed on this page. A couple of practical limits also apply:

  • A script may run for at most 10 seconds.

  • A script may execute at most 10,000 steps.

If a script hits a limit or raises an error, it stops and the error is written to the rule's log.

Triggers

A rule has exactly one trigger — the event that runs its script. This section describes each trigger, what it is useful for, the variables the script receives, and a short example.

Each variable is an object whose fields you read with a dot (for example submission.verdict); follow the link on a variable to see its fields. Three variables are always present, so they are not repeated below: space (the space), event_name (a short description of the event), and trigger_name (the trigger). Related objects are included when they can be looked up. The functions used in the examples are described under Functions.

Submission completed

Fires when a submission finishes judging and receives a verdict. Use it to react to individual solutions — grant an achievement for a first accepted submission, flag a member who cleared a hard problem, or keep a running tally of attempts.

Variables: submission

# Grant an achievement for an accepted submission, once per problem
if submission.verdict == "ACCEPTED":
    eolymp_achievements_assign(
        member_id = submission.member_id,
        achievement_id = "<achievement-id>",
        reference = submission.problem_id,
    )
    printf("achievement granted to %s", submission.member_id)

Contest submission completed

Fires when a submission made within a contest finishes judging. Like Submission completed, but scoped to a contest — it carries the contest and the participant, and its submission is a contest submission. Use it to react specifically to contest solutions.

Variables: submission, contest, participant, member

# Grant an achievement when a contest submission is accepted
if submission.verdict == "ACCEPTED":
    eolymp_achievements_assign(
        member_id = member.id,
        achievement_id = "<achievement-id>",
        reference = submission.problem_id,
    )

Contest finalized

Fires after a contest is finalized. Because the contest is already frozen, use this trigger to react to the finished contest — grant participation achievements, kick off post-processing, or announce that results are ready. To change the contest or its participants, use the Contest action trigger before finalizing.

Variables: contest

# Grant a participation achievement to everyone in the contest
total, participants = eolymp_participants_list(contest_id = contest.id, size = 500)
for p in participants:
    if not p.ghost:
        eolymp_achievements_assign(
            member_id = p.member_id,
            achievement_id = "<participation-achievement-id>",
            reference = contest.id,  # once per contest
        )

Contest score changed

Fires when a participant's score changes during a contest. Use it to react as a contest unfolds — award a badge the moment someone solves every problem, or watch for a target score.

Variables: score, contest, participant, member

# Award a badge when a participant solves every problem
if contest.problem_count > 0 and score.solved == contest.problem_count:
    eolymp_achievements_assign(
        member_id = member.id,
        achievement_id = "<all-solved-achievement-id>",
        reference = contest.id,
    )

Participant registered

Fires when someone registers as a participant in a contest. Use it to set up new entrants — send a welcome, assign a starting group, or grant extra time to a particular cohort.

Variables: participant, contest, member

# Add every new entrant to a group
eolymp_members_add_group(
    member_id = member.id,
    group_id = "<contestants-group-id>",
)
printf("%s registered for %s", participant.display_name, contest.name)

Participant finalized

Fires after a participant's result in a contest is finalized. Because the result is already frozen, use this trigger to react to the final standing — grant an achievement for a top finish, record the result somewhere, or notify the participant. Changing the participant (assigning a medal, marking them official or unofficial, granting extra time) will not affect a finalized result — do that before finalizing with the Contest action trigger instead.

Variables: participant, result, contest, member

# Grant an achievement for a top-three finish
if result.rank <= 3 and not result.unofficial:
    eolymp_achievements_assign(
        member_id = result.member_id,
        achievement_id = "<achievement-id>",
    )
    printf("top-3 achievement granted to %s", result.name)

Member changed

Fires when a member joins your space or an existing member is later changed. member is the member's current state; previous is their state before the change. previous is included only for updates — it is not provided when a member has just joined, which is how a rule tells a new member from a changed one. Use it to onboard new members (add them to a group, set attributes, grant a welcome achievement) or to react to profile changes.

Variables: member, previous

# Route new members into a group based on their grade attribute
if member.attributes.grade > 9:
    eolymp_members_add_group(
        member_id = member.id,
        group_id = "<senior-group-id>",
    )
else:
    eolymp_members_add_group(
        member_id = member.id,
        group_id = "<junior-group-id>",
    )

Question created or replied

Fires when a question is asked or answered in a contest. Use it to help with support — flag new questions, notify staff, or triage by subject. The reply variable is present only when the event is a reply. (Scripts can read questions but cannot reply to them; to reply automatically, use an AI agent action.)

Variables: ticket, reply, contest, member

# Log newly created questions
if ticket.reply_count == 0:
    printf("new question from %s: %s", member.display_name, ticket.subject)

Contest action

A manual trigger you run on demand against a specific contest, using the Trigger button next to the rule. This is the right place to adjust a contest's participants — mark entries official or unofficial, grant extra time, or set a medal on a participant — usually before you finalize the contest. It is also handy for one-off jobs and for testing a rule before you rely on it.

Variables: contest

# Make every unofficial entry official, before finalizing the contest
total, participants = eolymp_participants_list(contest_id = contest.id, size = 500)
for p in participants:
    if p.unofficial:
        eolymp_participants_set_official(
            contest_id = contest.id,
            participant_id = p.id,
            official = True,
        )

Member action

A manual trigger you run on demand against a specific member. Use it for one-off member jobs and for testing.

Variables: member

# Grant an achievement to the selected member
eolymp_achievements_assign(
    member_id = member.id,
    achievement_id = "<achievement-id>",
)

Functions

Call functions with keyword arguments, for example eolymp_members_add_group(member_id = "...", group_id = "..."). Arguments marked <optional> can be omitted. Functions that read data return a value; functions that change data return nothing.

Achievements

eolymp_achievements_assign(member_id, achievement_id, qty=<optional>, inc_by=<optional>, reference=<optional>)

Grant an achievement to a member. By default the member's count is set to 1. Pass qty to set the count to an exact number, or inc_by to increase it by an amount. reference is an optional key: repeated runs that use the same reference apply only once for that key — handy to avoid granting twice for the same thing.

Members

eolymp_members_get(id)

Fetch a member by id. Returns a member.

eolymp_members_list(filters=<optional>, search=<optional>, size=<optional>, offset=<optional>, sort=<optional>, order=<optional>)

List members in the space. Returns the total count and a list of member objects — see Listing and filters.

Filterable fields: id, external_ref, type, display_name, inactive, incomplete, unofficial, seated, team_id, group_id, user_issuer, user_subject, user_email, user_name, user_nickname, birthday, country, score, attribute

eolymp_members_set_attributes(member_id, attributes)

Merge profile attributes into a member. attributes is a dictionary of attribute keys to text or numbers; keys you don't list are left unchanged.

eolymp_members_set_preferences(member_id, locale=<optional>, timezone=<optional>, runtime=<optional>)

Update a member's preferences. Only the fields you pass are changed. runtime is the member's default language for solutions.

eolymp_members_add_group(member_id, group_id)

Add a member to a group.

eolymp_members_remove_group(member_id, group_id)

Remove a member from a group.

eolymp_members_set_active(member_id, active)

Enable (active = True) or disable (active = False) a member.

Participants

eolymp_participants_get(contest_id, participant_id)

Fetch a participant in a contest. Returns a participant.

eolymp_participants_list(contest_id, filters=<optional>, search=<optional>, size=<optional>, offset=<optional>, sort=<optional>, order=<optional>)

List a contest's participants. Returns the total count and a list of participant objects. Sorting is limited to display_name and started_at; there is no sort by score or rank.

Filterable fields: id, member_id, group_id, status, started_at, unofficial, disqualified, inactive, role, staff, has_violations

eolymp_participants_set_official(contest_id, participant_id, official)

Mark a participant official (official = True) or unofficial (official = False).

eolymp_participants_set_medal(contest_id, participant_id, medal)

Set a participant's medal. medal is one of "gold", "silver", "bronze", "honorable_mention" or "none".

eolymp_participants_set_extra_time(contest_id, participant_id, seconds)

Grant a participant extra (bonus) time, in seconds.

eolymp_participants_set_active(contest_id, participant_id, active)

Enable (active = True) or disable (active = False) a participant.

eolymp_participants_disqualify(contest_id, participant_id, requalify=<optional>, reason=<optional>)

Disqualify a participant; pass requalify = True to reverse it. reason is optional explanatory text.

These functions change a contest's participants and are best run from a Contest action rule before the contest is finalized.

Contests

eolymp_contests_get(id)

Fetch a contest by id. Returns a contest.

eolymp_contests_list(filters=<optional>, search=<optional>, size=<optional>, offset=<optional>)

List contests in the space. Returns the total count and a list of contest objects.

Filterable fields: id, name, starts_at, ends_at, public, visibility, format, status, featured, year, scale, series, difficulty, country, region, city, member_id

eolymp_contests_get_submission(contest_id, id)

Fetch a submission made within a contest. Returns a contest submission.

eolymp_contests_list_submissions(contest_id, filters=<optional>, after=<optional>, size=<optional>, offset=<optional>)

List a contest's submissions. Returns the total count and a list of contest submission objects.

Filterable fields: id, participant_id, problem_id, status, runtime, score, percentage, submitted_at, signature, verdict

Problems

eolymp_problems_get(id)

Fetch a problem by id. Returns a problem.

eolymp_problems_list(filters=<optional>, search=<optional>, size=<optional>, offset=<optional>, sort=<optional>, order=<optional>)

List problems in the space. Returns the total count and a list of problem objects.

Filterable fields: id, topic_id, is_visible, is_private, number, difficulty, status, score, is_bookmarked

Submissions

eolymp_submissions_get(id)

Fetch a submission by id. Returns a submission.

eolymp_submissions_list(filters=<optional>, after=<optional>, size=<optional>, offset=<optional>)

List submissions in the space. Returns the total count and a list of submission objects.

Filterable fields: id, problem_id, user_id, member_id, submitted_at, runtime, status, verdict, score, percentage

eolymp_submissions_aggregate(group_by=<optional>, filters=<optional>, metric=<optional>, range_start=<optional>, range_end=<optional>)

Summarise submissions into buckets. group_by is one or more dimensions to group by — "SUBMITTED_AT", "VERDICT", "STATUS" (pass a list for several). metric is what to compute per bucket; currently only "COUNT" (the default). range_start and range_end are RFC 3339 timestamps bounding the submissions considered; if omitted, only the last 30 days are counted. Returns a list of buckets, each an object with dimensions (the group-by values for that bucket) and count.

Filterable fields: problem_id, member_id, user_id, verdict, runtime, status, score, percentage

Groups

eolymp_groups_get(id)

Fetch a group by id. Returns a group.

eolymp_groups_list(filters=<optional>, size=<optional>, offset=<optional>)

List groups in the space. Returns the total count and a list of group objects.

Filterable fields: id, external_ref, name, query

Pages

eolymp_pages_get(id)

Fetch a content page by id. Returns a page.

Time

Timestamps in a payload are RFC 3339 strings, and Starlark has no clock or duration type — these helpers let a rule reason about time (for example, "within five minutes of the deadline").

time_diff(a, b)

Return a − b in seconds (a whole number), negative when a is earlier than b. Both arguments are RFC 3339 timestamp strings.

time_shift(timestamp, seconds)

Shift timestamp by the given number of seconds (negative to go back) and return the result as an RFC 3339 string.

Logging

Logging lets you trace what a script did; the output appears in the rule's logs.

printf(format, *args)

Format a message and add it to the log. Use %s for text, %d for whole numbers, and %v for any value.

print(*args)

Add a plain message to the log.

Listing and filters

Every ..._list function returns two values: the total number of matching items and a list of the items on the current page. Unpack them together:

total, members = eolymp_members_list(size = 5)
printf("space has %d members", total)
for m in members:
    printf("- %s", m.display_name)

size and offset control paging. search, sort, and order ("asc" or "desc") are available where supported.

Filters are passed as a dictionary keyed by field name. Each field maps to an operator and a value; a bare value is shorthand for equality. The field names below are illustrative; each list's own filterable fields are noted with its function above.

{"verdict": "ACCEPTED"}                         # equals
{"level": {"gte": 5}}                           # greater than or equal
{"display_name": {"contains": "team"}}          # substring match
{"created_at": {"gt": "2026-01-01T00:00:00Z"}}  # after a date (RFC 3339)

Available operators:

Operator

Meaning

eq

equals (also the default)

ne / neq

not equal

gt, ge / gte

greater than, greater or equal

lt, le / lte

less than, less or equal

contains

contains substring

starts / prefix

starts with

Values may be text, whole numbers, True/False, or an RFC 3339 timestamp string such as "2026-01-01T00:00:00Z".

Dry run

When a rule runs as a dry run, read-only functions (..._get, ..._list) work as usual, but the first function that would change data is recorded in the log without being performed, and the script then stops. Use a dry run to check what a script would do before letting it act for real.

Objects

The fields on each object returned by a function or passed in as a trigger variable. Read a field with a dot, e.g. submission.verdict.

submission

Field

Type

id

text

problem_id

text

user_id

text

member_id

text

lang

text (short language, e.g. cpp)

runtime

text (full runtime id)

status

text

verdict

text

score

number

cost

number

percentage

number

submitted_at

timestamp

judged_at

timestamp

contest submission

A submission made within a contest, returned by eolymp_contests_get_submission and eolymp_contests_list_submissions. Its problem_id is the contest's problem (not the archive problem), and it also carries the contest and the participant.

Field

Type

id

text

contest_id

text

problem_id

text

participant_id

text

lang

text (short language, e.g. cpp)

runtime

text (full runtime id)

status

text

verdict

text

score

number

cost

number

percentage

number

deleted

boolean

submitted_at

timestamp

member

Field

Type

id

text

external_ref

text

display_name

text

rank

text

rating

number

level

number

inactive

boolean

attributes

object (attribute key → text or number)

user_name

text

user_nickname

text

user_email

text

user_email_verified

boolean

user_picture

text

user_city

text

user_country

text

contest

Field

Type

id

text

key

text

slug

text

name

text

url

text

image_url

text

format

text

series

text

status

text

visibility

text

participation_mode

text

duration

number (seconds)

starts_at

timestamp

ends_at

timestamp

problem_count

number

participant_count

number

allow_upsolve

boolean

require_admission

boolean

participant

Field

Type

id

text

member_id

text

display_name

text

role

text

status

text

unofficial

boolean

ghost

boolean

inactive

boolean

disqualified

boolean

finalized

boolean

started_at

timestamp

end_at

timestamp

result

Field

Type

participant_id

text

member_id

text

contest_id

text

name

text

unofficial

boolean

disqualified

boolean

ghost

boolean

medal

text

rank

number

rank_lower

number

score

number

penalty

number

solved

number

score

Field

Type

score

number

penalty

number

solved

number

upsolve

boolean

breakdown

list of objects (see below)

Each item in breakdown has:

Field

Type

problem_id

text

solved

boolean

score

number

percentage

number

attempts

number

ticket

Field

Type

id

text

contest_id

text

member_id

text

participant_id

text

subject

text

message

text

status

text

reply_count

number

created_at

timestamp

updated_at

timestamp

last_reply_at

timestamp

reply

Field

Type

id

text

ticket_id

text

author

text (participant or jury)

member_id

text

user_id

text

message

text

created_at

timestamp

problem

Field

Type

id

text

url

text

type

text

number

number

title

text

language

text

languages

list of text

topics

list of text

difficulty

number

score

number

acceptance_rate

number

submissions_count

number

submissions_accepted

number

author

text

source

text

origin

text

visible

boolean

group

Field

Type

id

text

name

text

description

text

external_ref

text

icon

text

badge

text

color

text

space

Field

Type

id

text

key

text

name

text

url

text

status

text

page

Field

Type

id

text

path

text

locale

text

title

text

draft

boolean

labels

list of text