Developer docs Guides

Background services

Scripts that react to project-status changes: the on_change convention, trigger scoping, and how runs are recorded.

What a service is

A background service binds a script (Python or TypeScript) to a workspace. Instead of a schedule or a manual trigger, it reacts: whenever a reactive store in the project status changes, the platform runs the service’s on_change handler with what changed.

The on_change convention

Define on_change(mutation, state) in the service script. It runs in the workspace sandbox with the same injected module as task scripts — client, reporter, create_task, and friends — plus the change context:

def on_change(mutation, state) -> None:
    # mutation: the change that fired this run (includes its events)
    # state: snapshot of the store after the change
    if state.get("stage") == "deploying":
        reporter.set_phase("Notifying")
        client.post("/projects/$PROJECT_ID/tasks", {
            "identifier": "notifier",
            "params": {"channel": state["channel"]},
        })

Trigger scoping

SettingBehaviour
Watched stores (empty = all)Narrow which reactive stores fire this service
EnabledStop and resume reaction without deleting the definition
WorkspaceWhich workspace the handler runs against
Custom imageOptional environment for the handler

How runs are recorded

Every trigger is a first-class run: the platform records a real task instance (kept off the kanban) with its phase, logs, and result — open the service’s run history to audit what fired and why. Inside the handler, BG_SERVICE_ID and ALLYBUILD_WORKSPACE_ID identify the service context.

Services in templates

Service definitions are one of the four configuration entities, so they ship with publish and fork like meta-tasks, agents, and crons — a forked team reacts out of the box.