React to ShotGrid status changes with the event daemon instead of polling
Stop polling ShotGrid for status changes. Use the event daemon and EventLog to trigger updates only when data changes, with idempotency checks and a logging-first rollout.
12 Jul 2025, 13:33 UTC

Polling ShotGrid is the wrong default
A common automation starts with a cron job: every five minutes, query Asset or Shot records, compare sg_status_list to the previous value, and push changes to a render queue, Slack channel, or internal asset database. Most polls return nothing. API quota is spent asking "what changed?" when the answer is usually "nothing." Worse, if an artist flips a status and flips it back between polls, your worker never sees the transition at all.
ShotGrid already records many data changes in its EventLog. Instead of repeatedly asking what changed, you can run the ShotGrid event daemon, a long-running process that subscribes to those records and invokes your callback when a relevant event arrives. This is a supported integration path for reacting to data changes rather than polling for them.
What the daemon actually processes
When ShotGrid data changes, the site writes an EventLogEntry. The daemon reads that stream using a cursor, which remembers the last event it processed. Each event includes an event type, an entity reference such as a Shot or Asset, and a meta payload describing what changed. Your callback receives the ShotGrid connection and one event record, then decides whether to act.
The payload shape varies by entity type and ShotGrid version, so treat field names as hypotheses until you inspect real events from your site. A script user with read access to EventLogEntry is required, and you should test with least-privilege credentials on a non-production site.
Comparison: polling versus event daemon
| Approach | Behavior | Main cost |
|---|---|---|
| REST or Python API polling | Repeatedly queries Asset or Shot status fields | API quota, avoidable latency, missed fast transitions |
| ShotGrid event daemon | Subscribes to EventLog entries and invokes a callback | Extra process to deploy, monitor, and restart |
Worked example: logging-first status handler
Start with a narrow plugin that handles one entity type and one field. The skeleton below listens for asset changes, checks whether sg_status_list moved to your approved value, and logs instead of writing to external systems. Run this on a daemon host with network access to your ShotGrid sandbox.
def handle_asset_change(sg, event, logger):
meta = event.get("meta") or {}
# Verify these keys against a real event from your sandbox before enabling side effects.
if meta.get("attribute_name") != "sg_status_list":
return
if meta.get("new_value") != "apr":
return
event_id = event.get("id")
entity = event.get("entity", {})
# Idempotency: key downstream work by event_id so a repeated delivery
# does not create duplicate updates.
logger.info("Approved asset event %s for entity %s", event_id, entity)
Placeholders to replace: sg_status_list is your status field name, "apr" is an example status short code, and mark_external_system would be your call to the render queue or asset database. Do not hardcode these values until you have printed a real EventLogEntry from your site and confirmed the keys.
Diagnostic check before enabling side effects
Run this on an admin workstation or CI runner that can reach your ShotGrid site. It requires a ShotGrid script user with read access to EventLogEntry. The query is read-only, but invalid credentials will fail authentication.
import shotgun_api3
sg = shotgun_api3.Shotgun(
"https://YOUR_SITE.shotgrid.autodesk.com",
script_name="YOUR_SCRIPT_NAME",
api_key="YOUR_SCRIPT_KEY",
)
latest = sg.find_one(
"EventLogEntry",
[],
{"order_by": [{"field": "id", "direction": "desc"}]},
["id"],
)
print("Latest EventLogEntry id:", latest["id"] if latest else None)
Expected check: the script prints a numeric event id. If it returns nothing, confirm the script user can see EventLogEntry and that your site has recent events. Compare this id with the daemon's processed cursor after running the logging plugin for a day; a large gap means the daemon is falling behind.
Trade-offs and limitations
- Delivery is at-least-once, not exactly-once. The same event can be delivered more than once. Consumers should deduplicate by event id and tolerate replays.
- Cursor state matters. The daemon tracks progress with a cursor. If that state is lost, behavior depends on event retention and your replay logic; you may reprocess old events or miss events that aged out of the log.
- Operational overhead. The daemon is another process to deploy, monitor, and restart. A slow callback or unavailable downstream system causes lag.
- Payload variance. Event meta fields differ by entity type and ShotGrid version. Inspect real events before writing conditional logic against specific keys.
Webhooks can be simpler than a daemon, but availability, limits, and payload behavior are version- and plan-sensitive. Verify current ShotGrid documentation before depending on webhooks instead of the event daemon.
Safe rollout and how to verify
- Run the daemon against a sandbox ShotGrid site with a logging-only plugin.
- Record every event for a day, then compare the daemon's processed last event id with the site's newest EventLog id to estimate lag.
- Repeat the same status change twice and confirm the downstream handler dedupes by event id instead of creating duplicate side effects.
- Keep a periodic reconciliation job as a correctness backstop, not as the primary sync path.
Only after those checks pass should you enable writes to your render queue, Slack integration, or asset database. Because enabling side effects changes state outside ShotGrid, rollback is straightforward: stop the daemon, remove or disable the plugin, and revert any configuration that points it at production endpoints.
Start small, log everything, and verify the cursor before you let the daemon touch external systems.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.