Resolve Atera tickets generated by alerts when the underlying alerts are resolved.
- Python 3.12
- httpx, pydantic, tenacity, typer
- Docker for packaging
Copy .env.example to .env, then set ATERA_API_TOKEN and ATERA_TECHNICIAN_ID to your own values. Keep .env out of Git.
Docker Compose passes .env into the container via env_file.
Requests authenticate with Authorization: Bearer <token>.
The API base URL is https://api.atera.com/api/v3. All requests, including pagination, are restricted to https://api.atera.com on port 443 without embedded URL credentials. Redirects are not followed.
For pagination only, legacy HTTPS links on app.atera.com are rewritten to the API host when they refer to the same endpoint. The path and query parameters are preserved; credentials are never sent to the legacy host. Relative pagination links are also supported.
API reference: Atera developer portal and its OpenAPI specification.
Ticket comments require ATERA_TECHNICIAN_ID in .env (existing Atera technician ID).
Linux/macOS:
cp .env.example .envWindows PowerShell:
Copy-Item .env.example .env- In Atera, create a token with Alerts Read and Tickets Read/Write permissions. If you restrict IPs, include the public IP of the machine running this job. Record the token expiration for rotation.
- Pause scheduled sync runs for the cutover. In the deployment's
.env, replaceATERA_API_KEYwithATERA_API_TOKENand set the new token as its value. Keep the token out of Git, logs, and tickets. The old key is not accepted by this version of the client. - If running in Docker, rebuild the image with
docker compose buildso it contains the updated client. - Validate ticket read access with
atera-sync test(ordocker compose run --rm atera-sync test). This command fetches tickets9981and9956and writes their full responses tofixtures/; protect or remove those files according to your data-handling policy. A404for a missing ticket does not by itself indicate an authentication failure. This test does not check Alerts Read or Tickets Write. - Run the normal sync under supervision to check alert reads and, when an eligible ticket exists, ticket comments and status updates. The
runcommand can modify tickets. Record any permissions that were not exercised as pending validation before resuming scheduled runs.
To roll back before the legacy key expires, restore the previous application version and its ATERA_API_KEY setting together.
If you have uv:
uv venv
uv pip install -e .Otherwise:
python -m venv .venv
source .venv/bin/activate
pip install -e .Local:
source .venv/bin/activate
atera-sync testDocker:
docker compose run --rm atera-sync testThis fetches tickets 9981 and 9956, prints a redacted summary (ticket id, status, source, alert id), and saves full JSON to fixtures/.
If any request fails, it prints the status code + response body and exits nonzero.
If ticket fixtures do not include alert ids, fetch an alert by id and save a sample fixture:
atera-sync alert-sample <alert_id>Export full open alerts list:
atera-sync alerts-exportLocal:
source .venv/bin/activate
atera-sync run 100 14Docker:
docker compose run --rm atera-sync run 100 14Run arguments are positional:
atera-sync run <max_alerts> <days_back>
pytestTests load fixtures from fixtures/. Run the test command first to populate ticket fixtures:
docker compose run --rm atera-sync test0 8-18 * * * cd /home/docker/AteraAlertResolver && /usr/bin/docker compose run --rm atera-sync run 100 14 >> /home/docker/AteraAlertResolver/sync.log 2>&1Create a daily task that runs hourly from 08:00 to 18:00 with the action:
docker compose run --rm atera-sync run 100 14
Set the “Start in” directory to:
C:\path\to\AteraAlertResolver
- Alerts are listed with status
Resolvedduringatera-sync runand used to discover related ticket ids. atera-sync runuses positional args (max_alerts days_back); current production setting is100 14.- If older resolved alerts were missed (for example after downtime/backlog), run a one-off catch-up with
atera-sync run 500 14, then return to100 14. - Alerts are actionable only when severity is
WarningorCritical;Informationalerts are skipped. - Pagination uses
itemsInPage=50and followsnextLinkuntil empty, accumulatingitems. max_alertsis applied during pagination to avoid pulling full alert history when capped.- For each resolved alert, full alert details are fetched to extract linked
ticketId. - Alerts are considered actionable only if alert status equals
resolved(case-insensitive), with fallback to the list filter status when not present. - Before closing a ticket, automation posts the comment:
alert resolved, closing ticket by automation. - Tickets are updated to
Resolvedonly if not already in terminal statesResolved/Closed/Merged/Deleted. - Each ticket is processed at most once per run even if multiple resolved alerts reference the same ticket.
- End-of-run summary logs include:
alertsReviewedticketsActionedupdatedTickets