Skip to content

Action templates and triggers

Action templates let you save a reusable job configuration. Instead of creating the same Job repeatedly, you create an ActionTemplate once, select its ProcessingStep, configure parameters and input WorkData, and then execute it whenever needed.

Cron triggers are schedules for action templates. When a trigger fires, the Job Management API creates one Job for each template linked to the trigger. This is useful for recurring reports, periodic simulations, nightly data processing, or any other repeated execution.

The Python client exposes this feature through the convenience classes:

from pinexq.client.job_management import (
ActionTemplate,
ActionTemplateQuery,
CronTrigger,
CronTriggerQuery,
Job,
JobQuery,
ProcessingStep,
)
from pinexq.client.job_management.model.open_api_generated import CronJobConflictResolutionOptions

The examples below assume that client is an already configured PineXQ client. See Set up the Client for authentication and client setup.

An action template starts from a ProcessingStep. You can select the step by name with ProcessingStep.from_name(), then pass its self-link to the template.

from pinexq.client.job_management import ActionTemplate, ProcessingStep
processing_step = ProcessingStep.from_name(
client=client,
step_name="my_function",
version="1.0.0",
)
template = ActionTemplate(client).create(
name="daily-report-template",
description="Create the daily report from the selected input data.",
processing_step_url=processing_step.self_link(),
)

The returned template is a convenience wrapper around the server resource. The current server representation is available as template.action_template_hco.

If the ProcessingStep has parameters, configure their template defaults with configure_parameters(). The keyword arguments should match the ProcessingStep parameter names.

template.configure_parameters(
region="DE",
include_plots=True,
max_iterations=100,
)

To remove the template-specific parameter configuration and fall back to the ProcessingStep defaults:

template.clear_parameters()

Templates can also store input WorkData selections. The slot index is zero-based and follows the ProcessingStep input dataslot order.

For a single-input dataslot, pass one WorkData URL:

template.select_dataslot_workdata(
slot_index=0,
work_data_urls=["https://jobs.api.pinexq.net/api/WorkData/019c0000-0000-0000-88ac-d08c000000000"],
)

For a collection dataslot, pass all selected WorkData URLs:

template.select_dataslot_workdata(
slot_index=0,
work_data_urls=[
"https://jobs.api.pinexq.net/api/WorkData/019c0000-0000-0000-88ac-d08c000000001",
"https://jobs.api.pinexq.net/api/WorkData/019c0000-0000-0000-88ac-d08c000000002",
],
)

To clear a dataslot selection:

template.clear_dataslot(slot_index=0)

After changing dataslots, template.action_template_hco.is_configured indicates whether the template has all required configuration needed for execution.

Templates support three tag groups:

  • Template tags: tags on the template resource itself, useful for finding templates.
  • Job tags: tags copied to every Job created from the template.
  • Output tags: tags copied to output WorkData produced by Jobs created from the template.
template.set_tags(["daily-report", "production"])
template.set_job_tags(["created-by-template", "daily-report"])
template.set_output_tags(["daily-report-output"])

These tags are useful when querying for created Jobs or WorkData later.

Use execute_now() when you want the template to create a Job immediately without creating a recurring schedule. The method returns a link to the created Job.

job_link = template.execute_now()
job = Job.from_hco(job_link.navigate())
job.wait_for_completion(timeout_s=600.0)
result = job.get_result()

A cron trigger has a title, description, cron expression, conflict behavior, and optional time zone. Create it disabled first, link templates, then enable it. This avoids a trigger firing before it has all templates attached.

from pinexq.client.job_management import CronTrigger
from pinexq.client.job_management.model.open_api_generated import CronJobConflictResolutionOptions
trigger = CronTrigger(client).create(
title="daily-report-trigger",
description="Runs the daily report template every weekday morning.",
cron_expression="0 9 * * 1-5",
conflict_resolution=CronJobConflictResolutionOptions.skip,
time_zone_id="Europe/Berlin",
enabled=False,
)
trigger.set_templates([template])
trigger.enable()

CronJobConflictResolutionOptions has two values. CronJobConflictResolutionOptions.stack_up creates the next Job immediately even if a previous run is still active while CronJobConflictResolutionOptions.skip skips it in this case.

One trigger can link multiple templates:

trigger.set_templates([template_a, template_b, template_c])

Each fire creates one Job per linked template.

Triggers can have an activation window. starts_at and expires_at are local wall-clock date-times without a timezone suffix. They are interpreted in the trigger’s time_zone_id.

trigger = CronTrigger(client).create(
title="campaign-report-trigger",
description="Runs only during the reporting campaign.",
cron_expression="0 8 * * *",
conflict_resolution=CronJobConflictResolutionOptions.skip,
time_zone_id="Europe/Berlin",
starts_at="2026-06-01T08:00:00",
expires_at="2026-06-30T18:00:00",
enabled=False,
)

You can also set or clear the activation window after creating the trigger:

trigger.set_activation_window(
starts_at="2026-06-01T08:00:00",
expires_at="2026-06-30T18:00:00",
)
# clear both bounds
trigger.set_activation_window(starts_at=None, expires_at=None)

The HCO includes both the original wall-clock values and the server-resolved UTC values:

trigger.refresh()
print(trigger.cron_trigger_hco.starts_at)
print(trigger.cron_trigger_hco.starts_at_utc)
print(trigger.cron_trigger_hco.expires_at)
print(trigger.cron_trigger_hco.expires_at_utc)

If a trigger should remove one-off templates when it expires, set delete_orphaned_templates_on_expiry=True.

trigger.set_activation_window(
expires_at="2026-06-30T18:00:00",
delete_orphaned_templates_on_expiry=True,
)

This cleanup only applies when the trigger is deleted because it expired. Only templates that are not referenced by any other trigger are deleted. Input WorkData assigned to those templates is not deleted by deleting the template.

Common trigger changes are available directly on the convenience class:

trigger.set_title_and_description(
title="new-daily-report-trigger",
description="Runs the updated daily report schedule.",
)
trigger.set_cron_expression(
cron_expression="0 7 * * 1-5",
conflict_resolution=CronJobConflictResolutionOptions.skip,
time_zone_id="Europe/Berlin",
)
trigger.disable()
trigger.enable()

Use ActionTemplateQuery and CronTriggerQuery to find existing resources.

from pinexq.client.job_management import ActionTemplateQuery, CronTriggerQuery
templates = ActionTemplateQuery.create(
client,
tags_by_and=["daily-report"],
).to_list()
enabled_triggers = CronTriggerQuery.create(
client,
is_enabled=True,
).to_list()

Query helpers follow the same pattern as the other Python client query classes:

  • count() returns the total match count.
  • exists() returns whether at least one match exists.
  • first() returns the first match or None.
  • one() requires exactly one match and raises otherwise.
  • to_list() returns all matches as convenience objects.
  • iter() iterates over all matches across result pages.

Disable a trigger before deleting it if it might still fire.

trigger.disable()
trigger.delete()

Deleting a trigger does not delete linked templates unless the trigger is auto-deleted by the system due to expiry with delete_orphaned_templates_on_expiry=True.

Delete a template when it should no longer be used:

template.delete()

Deleting a template does not delete WorkData selected in its input dataslots.