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.
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.
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.
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)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,
)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
)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,
)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)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)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.
# 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>",
)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)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,
)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>",
)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.
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.
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.
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.
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
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
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
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
eolymp_pages_get(id)
Fetch a content page by id. Returns a page.
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 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.
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 |
|---|---|
| equals (also the default) |
| not equal |
| greater than, greater or equal |
| less than, less or equal |
| contains substring |
| starts with |
Values may be text, whole numbers, True/False, or an RFC 3339 timestamp string such as "2026-01-01T00:00:00Z".
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.
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.
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text (short language, e.g. |
| text (full runtime id) |
| text |
| text |
| number |
| number |
| number |
| timestamp |
| timestamp |
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 |
|---|---|
| text |
| text |
| text |
| text |
| text (short language, e.g. |
| text (full runtime id) |
| text |
| text |
| number |
| number |
| number |
| boolean |
| timestamp |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| number |
| number |
| boolean |
| object (attribute key → text or number) |
| text |
| text |
| text |
| boolean |
| text |
| text |
| text |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| number (seconds) |
| timestamp |
| timestamp |
| number |
| number |
| boolean |
| boolean |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text |
| boolean |
| boolean |
| boolean |
| boolean |
| boolean |
| timestamp |
| timestamp |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| boolean |
| boolean |
| boolean |
| text |
| number |
| number |
| number |
| number |
| number |
Field | Type |
|---|---|
| number |
| number |
| number |
| boolean |
| list of objects (see below) |
Each item in breakdown has:
Field | Type |
|---|---|
| text |
| boolean |
| number |
| number |
| number |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text |
| text |
| text |
| number |
| timestamp |
| timestamp |
| timestamp |
Field | Type |
|---|---|
| text |
| text |
| text ( |
| text |
| text |
| text |
| timestamp |
Field | Type |
|---|---|
| text |
| text |
| text |
| number |
| text |
| text |
| list of text |
| list of text |
| number |
| number |
| number |
| number |
| number |
| text |
| text |
| text |
| boolean |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text |
| text |
| text |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| text |
Field | Type |
|---|---|
| text |
| text |
| text |
| text |
| boolean |
| list of text |