Blizzard API Compliance¶
All modules must comply with Blizzard's API Terms of Use. These are legally binding requirements, not suggestions.
30-day data TTL¶
All Blizzard-sourced data must be refreshed or deleted within 30 days. Every synced record carries a freshness timestamp:
- Content entities use a
last_fetchedbase field. This includes catalogs (wow_character,wow_guild,wow_achievement,wow_item,wow_mount,wow_pet,wow_title,wow_toy) and per-owner junction rows (wow_achievement_progress,wow_collected_pet,wow_mount_collection,wow_reputation_standing,wow_title_award,wow_toy_collection). - Taxonomy terms synced from reference endpoints (
wow_realm,wow_playable_class,wow_playable_race,wow_playable_specialization,wow_power_type,wow_reputation_faction) use awow_last_fetchedconfigurable field. Drupal taxonomy terms can't host arbitrary base fields, so the wow submodules ship the field as config rather than declaring it on a custom entity.
The wow.ttl_sweeper service deletes content entities whose last_fetched exceeds the 30-day window. It runs from hook_cron, throttled to at most once per 23 hours (state key wow.ttl_sweep_last), or on demand via drush wow:sweep-ttl. Deletion happens in chunks of 50 entities to keep memory bounded. The SWEEP_ENTITY_TYPES constant on TtlSweeper enumerates every entity type swept — both catalog entities (wow_achievement, wow_character, wow_guild, wow_item, wow_mount, wow_pet, wow_title, wow_toy) and per-owner junction rows (wow_achievement_progress, wow_collected_pet, wow_mount_collection, wow_reputation_standing, wow_title_award, wow_toy_collection). The TtlSweepCoverageTest regression test scans every entity class for last_fetched and asserts each is registered in the sweep set, so adding a new catalog entity without wiring the sweep fails CI.
Junction entities need their own TTL because Blizzard's character profile API can return a partial collection (large accounts) and because un-collected items disappear from the API but stay in local storage until swept.
Taxonomy reference data is not swept — operators keep it fresh via the scheduled sync commands (drush wow:sync-realms etc.). Pair them with a cron schedule to stay inside the window.
Blizzard's Terms allow validating that a character ID still exists as a lighter-weight alternative to a full profile re-fetch. The suite does not currently use that path: CharacterSync::refresh() returns NULL on 403 / 404 without deleting the entity, and the TTL sweeper is the single deletion mechanism for stale characters. A validate-and-delete path is a reasonable future optimization, but it is not a current requirement — the 30-day sweep already satisfies the ToS contract.
Rate limiting¶
The API allows 36,000 calls per hour and 100 per second (Blizzard's documented hard caps). BattleNetClient enforces both ceilings — the per-hour and per-second counters are stored in Drupal State (wow.api_rate_limit, wow.api_rate_limit_second) so every PHP process (web, drush, queue workers) shares one view, and check + increment is serialized under the wow:api_rate_limit lock to prevent concurrent overshoot.
When either ceiling is full, get() throws a RuntimeException with a "rate limit reached" message — it does not sleep, retry silently, or return NULL. If the wow:api_rate_limit lock itself cannot be acquired after a few retries, the client refuses to mutate the counters and throttles the request rather than risk a concurrent overshoot — see lifecycle → Rate limiting for the mechanics. Queue-based syncs (achievements, mounts, pets, titles, toys) naturally pace themselves through the Advanced Queue processor, so rate-limit exceptions generally only surface for ad-hoc drush calls or misconfigured external callers.
Monthly call totals are tracked separately in State (wow.api_calls.YYYY_MM) as a diagnostic counter. They do not gate requests — only the hourly and per-second windows do.
Media caching¶
Blizzard strongly recommends caching icon and media assets locally rather than hotlinking their CDN. The cache_icons setting in the core module controls this behavior. When enabled, icons are downloaded to public://wow/<type>/ on first fetch and served from local storage.
User data deletion¶
If a user de-links their Battle.net account or requests account deletion, all their Blizzard-sourced data must be immediately purged. Two cascade paths are wired:
- Account deletion —
wow_user_entity_predeleteruns on the user entity and callswow_user_purge_account_data($uid), which deletes the user'swow_characterrows and clears OAuth tokens / selection state fromuser.data. - Battle.net de-link —
wow_user_form_openid_connect_accounts_form_alteradds a submit handler that calls the same purge function when the user clicks "Disconnect" for thebattlenetclient on/user/{uid}/connected-accounts. The alter is scoped to thebattlenetclient only so non-Blizzard OIDC disconnections don't trigger the purge.
Character deletion in turn fires wow_character_entity_predelete, which dispatches deleteCharacterData() to every registered CharacterDataProvider plugin (achievements, mounts, pets, titles, toys, reputation), purging all related collection data. One provider throwing does not block the rest — each is caught and logged. The same pattern applies to guilds via wow_guild_entity_predelete and GuildDataProvider (the interface ships today; no in-tree plugin implementations yet).
API key security¶
The client secret must never appear in configuration exports, logs, or error messages. The module enforces this by storing the secret via the Key module, which supports environment variables and file-based providers that keep secrets out of the database.
Attribution¶
You must credit Blizzard as the data source on any page displaying API-sourced data. You may not use Blizzard trademarks in your application's name or URL in a way that implies endorsement.
No monetization of API data¶
You may not charge for features that use Blizzard API data, place API-sourced content behind paywalls, serve forced ads alongside it, or sell the data to third parties.