# API

# Release Notes

<table border="1" id="bkmrk-2.0.0-%C2%A0" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 99.8718%;"></col></colgroup><tbody><tr><td>### 4.3.0

01\. May 26

This release has a lot of changes under the hood. Almost nothing should change for consumers of the API, but the entire parsing logic was revamped. The parser now relies on the attributes of the Lodestone CSS selectors and uses more uniform logic for different kinds of parses.

##### New

- SDK library licenses now shown on license page

##### Maintenance

- Revamped parser 
    - This should not have any consumer facing changes, but it will improve maintainability and reliability
- Total minion and mount numbers updated for 7.5
- Add gear rarity, icon links, and dye to tests
- Add facewear, soul crystal, and HP to tests

##### Fixed

- Gear parsing broken after 7.5 update 
    - Lodestone now loads gear lazily
- TownIcon was always null
- Log level for HttpClient was wrong in certain environments

</td></tr><tr><td>### 4.2.0

20\. Mar 26

This release switches to a fork of NetStone and the XIVAPI Lodestone CSS selectors. This allows for certain new features that did not get merged into the original projects. It is now possible to retrieve the facewear slot that got added in Dawntrail. Since Dawntrail had launched, retrieving the dye of gear was broken and hadn't been part of NetStone API. This is now fixed, and the second dye slot added. Lastly, gear and glamours now return icon URLs.

##### New

- Facewear 
    - To not introduce breaking changes and require an API V5, facewear is returned the same way other pieces of gear are. Since facewear is unlocked by a specific item, this item is returned as if it were a piece of glamour.
- Gear Dye 
    - Both dye slots are returned with their name, colour (hex code), and database link
- Icon URLs for gear 
    - Both the gear itself and glamour pieces now include icon URLs
    - Icon URLs are from the Lodestone itself
- Gear rarity 
    - The rarity of equipped gear is now returned
    - New enum: Common, Magic, Uncommon, Rare, Epic

##### Maintenance

- Switch to NetStone fork
- GitHub workflow now runs directly on runner to drastically speed up build times 
    - Build was previously running inside a Docker container

##### Fixed

- Kerberos library error during startup 
    - This was purely a cosmetical error and did not affect functionality
- Fix excessive logging of Microsoft.AspNetCore packages

##### Removed

- NetStone.Queue project 
    - RabbitMQ functionality was disabled in version 4.0.0, this removes the unused project

</td></tr><tr><td>### 4.1.3

28\. Dec 25

##### Fixed

- Compose file was not updated for version 4.1.2, now correctly points towards 4.1.3

</td></tr><tr><td>### 4.1.2

28\. Dec 25

##### Fixed

- Two libraries were missing from Licenses page
- Staging environment was pointing towards production instance of FFXIV ID

</td></tr><tr><td>### 4.1.1

28\. Dec 25

##### Changed

- "CacheImages" parameter now overrides "MaxAge" if images have never been cached before 
    - If images have been cached at any point, "MaxAge" behaves as usual

</td></tr><tr><td>### 4.1.0

28\. Dec 25

This release adds S3 storage for avatars and portraits. Previously, only the URL to the image on Square's servers was cached. If Square updated the images on their side, there would be a mismatch between all other cached data and these updated images. This version optionally allows for the images themselves to be cached separately.

##### New

- Optional "CacheImages" parameter for Character endpoint 
    - Avatar and portrait are saved to S3 storage
    - Checksum is checked each time to avoid duplicate uploads

</td></tr><tr><td>### 4.0.1

21\. Dec 25

##### Fixed

- Compose file was pointing to wrong URL in reverse proxy configuration
- Old MIT license file was still present

</td></tr><tr><td>### 4.0.0

21\. Dec 25

This is a huge maintenance release and changes various things under the hood. Most importantly, it introduces API V4. This version reorders API paths to follow REST standards, and expands fallback values. Multiple values can now be sent together for more granular control over when a fallback happens. The API now uses FFXIV ID for authentication, and runs on .NET 10.

##### New

- New homepage: [https://netstone.eorzea.online](https://netstone.eorzea.online)
- API V4 
    - Paths now follow rest standards 
        - "Character/{lodestoneId}/Minions" instead of "Character/Minions/{lodestoneId}"
    - Fallback Type is now a flags enum and offers more values 
        - Multiple values can be submitted together (this is not supported inside Swagger UI, but generally works)
- Use FFXIV ID for authentication

##### Changed

- Default API version is now 4 
    - This default would usually only change on a X.1 release, but this is a completely new deployment under a new URL

##### Maintenance

- Total minion and mount numbers updated for 7.4
- Runtime upgrade to .NET 10
- Switch to Apache-2.0 license
- Upgrade to new unit testing package
- Container images are now built with GitHub Workflow
- Container images are now pushed to GitHub Container Registry automatically
- GitHub releases are now created automatically
- Added global.json to better control .NET upgrades
- Compose files for Dev, Staging, and Production environments now tracked through repository

##### Fixed

- Character search test would return two characters and fail

##### Removed

- RabbitMQ and Jaeger tracing services disabled as they are currently unused 
    - Code is still available and services may be reactivated when necessary

</td></tr><tr><td>### 3.2.1

19\. Jun 25

Some oddities during Lodestone maintenances have happened previously, so I used the most recent maintenance window to fix some minor bugs.

##### Fixed

- API would return 404 Not Found for FC members if they were being refreshed from the Lodestone during maintenance
- Character and Free Company search threw 500 instead of returning 503 during Lodestone maintenance

</td></tr><tr><td>### 3.2.0

20\. May 25

This release extends fallback support by handling the more granular privacy settings the Lodestone added. New endpoints allow retrieving characters and free companies from cache by name, and the landing page now shows libraries and their licenses.

##### New

- Support for more granular privacy settings on profile 
    - 503 now returned if profile generally accessible, but details are hidden
- Endpoints for retrieving characters and free companies from cache by name 
    - /Character/ByName/{name}/{world}
    - /FreeCompany/ByName/{name}/{world}
    - Result is always loaded from cache
- Libraries and their licenses shown on [https://netstone.api.tawmy.net/libs](https://netstone.api.tawmy.net/libs)

##### Changed

- Mount and minion counts now loaded from configuration instead of requiring code changes
- 404 response codes not printed to console anymore

##### Removed

- API V2

</td></tr><tr><td>### 3.1.1

30\. Apr 25

##### Changed

- Total minion and mount numbers updated for 7.21

##### Fixed

- GlamourName and CreatorName returned empty string instead of being null if not set 
    - Properties were already nullable, now they actually return null
    - Potentially a breaking change if client does not handle nullability for these properties correctly

</td></tr><tr><td>### 3.1.0

20\. Apr 25

Version 3.0.0 added fallback support for when the Lodestone is entirely unreachable. This version extends this by also allowing fallbacks when the Lodestone is down for maintenance or when a profile is private. If no fallback is used, a 503 HTTP Status Code is returned.

A new .NET SDK replaces the existing API Client, and there is various maintenance-related changes too.

##### New

- .NET SDK 
    - Rebuilt from the ground up for dependency injection, this new SDK replaces the existing NetStone API Client.
    - Changelog [here](https://wiki.tawmy.net/books/netstone-api/page/release-notes-IW4)
    - Basic usage guide [here](https://github.com/Tawmy/NetStone.Api.Sdk)
- Handle private profiles and Lodestone maintenance 
    - NetStone returns an empty object when either of these apply
    - API now has a check for when essential properties are empty and returns an 503 HTTP Status Code
    - When FallbackType is set to Any, fallback is used
    - Check added for Free Companies as well, but is untested
- Data protection for ASP.NET Core encryption keys 
    - Keys are encrypted using X.509 certificate and stored in database
- Health checks for database and data protection certificate expiry
- Tracing data now recorded for mapping extensions
- Global exception handler for HTTP Status Code 404 and 503 results

##### Changed

- Default API version is now 3
- API version 2 marked as obsolete 
    - Will be removed in a future release
- When retrieving free company members, full character profiles are only queued once 
    - They were previously queued per-character, this should greatly improve performance
- Parsing class jobs throws more appropriate exception
- Remove redundant Console.WriteLine calls to prevent duplicate exception output

</td></tr><tr><td>### 3.0.1

24\. Mar 25

##### Changed

- useFallback parameter accepts "None", "Http", and "Any" values now 
    - Previously it was simple bool
    - "Any" behaves as previous "true", catching all exceptions
    - "Http" catches HttpRequestExceptions only 
        - HttpRequestException is thrown when connection to the Lodestone fails (eg. when it's down, rate limited, etc)
        - Other exceptions are likely to be parsing bug and swallowing them may not be desired

##### Fixed

- Landing page "OpenAPI Spec" button was opening V2 API spec

##### Maintenance

- Fallbacks are logged as warnings

##### Breaking Changes

- useFallback type changed from bool to FallbackType enum

</td></tr><tr><td>### 3.0.0

23\. Mar 25

This version adds fallback support in case the Lodestone is unreachable. It also adds OpenTelemetry metrics and tracing support. Metrics give me insight in how much the API is used and whether it can process all requests. Tracing data allows me to tune performance and if things went wrong, see where they did.

##### New

- API V3 with fallback support 
    - New useFallback parameter
    - If set and parsing fails, API returns cached data
    - New V3 DTOs have fallbackUsed and fallbackReason properties for transparency
    - AutoMapper removed in API V3, replaced with manual mapping
- OpenTelemetry Metrics 
    - Helps getting an overview over how much each endpoint is used, how long requests take on average, and how many fail
- OpenTelemetry Tracing 
    - Helps getting insight into how long each part of a request takes exactly, for performance optimisations
- New unit tests comparing API V2 and V3 responses

##### Maintenance

- Runtime upgrade to .NET 9
- Switch to MIT license

##### Fixed

- Free Company focus mapping
- Character not assigned to achievements if retrieved after achievements were already cached
- Error while retrieving free company members in edge case where character was retrieved before, but free company was not
- Free Company not always updated when player changed/left FC

##### Removed

- API V1

##### Breaking Changes

- API V1 removed
- None in Grand Company affiliation renamed to NotSpecified

</td></tr><tr><td>### 2.1.2

03\. Jan 25

##### Fixed

- Exception while attempting to cache character that had a name or world change<span style="color: rgb(187, 187, 187); font-family: var(--font-heading, var(--font-body)); font-size: 1.4em;"> </span>

</td></tr><tr><td>### 2.1.1

05\. Nov 24

##### Fixed

- Page margin too large on mobile
- Landing page scrolling horizontally on mobile
- Demo too wide on mobile once data loaded

</td></tr><tr><td>### 2.1.0

03\. Nov 24

##### New

- [Landing Page](https://netstone.api.tawmy.net/ "Landing Page")
    - Basic introduction to the project
    - Demo for retrieving data
    - Links to Changelog, Swagger UI, and OpenAPI definition
- "Data refreshed" exchanges 
    - Previous queues were published to queues whenever data was requested through queue, no matter whether it was cached or not
    - New exchanges are published to whenever data is refreshed from Lodestone, not when it was cached

##### Changed

- Outgoing queues are now exchanges 
    - See "Breaking Changes" for further details
- Default API version is now 2
- API version 1 marked as obsolete 
    - Will be removed in a future release
- Swagger UI now always available

##### Breaking Changes

- Outgoing RabbitMQ messages are now published to exchanges instead of queues 
    - Allows fanout exchange to publish messages to multiple queues
    - Requires manual configuration of exchange -&gt; queue binding
    - If RabbitMQ was used before, both exchange and queue will exist and only

</td></tr><tr><td>### 2.0.3

28\. Sep 24

##### Fixed

- Free company members failed to parse if at least one of its members had been cached before

</td></tr><tr><td>### 2.0.2

27\. Sep 24

##### Fixed

- Grand Company parsing fails under certain circumstances

</td></tr><tr><td>### 2.0.1

22\. Sep 24

##### Changed

- Version is printed to console on startup

</td></tr><tr><td>### 2.0.0

22\. Sep 24

Complete revamp of the API. Instead of simply parsing and returning NetStone data, Lodestone data is now mapped onto database entities and cached. Retrieving character data can now return cached data as DTOs, or refresh data from the Lodestone and return as said DTOs.

##### New

- Character achievement parsing
- NetStone entity to database mapping
- **Caching of database entities**
    - Character + attributes
    - Character ClassJobs
    - Character Minions
    - Character Mounts
    - Character Achievements
    - Free Company
    - Free Company Memberes
- **NetStone.Api.Client project for easy data retrieval in .NET projects**
- Various unit tests to compare parsed data and data mapped to database entities and DTOs
- Additional properties in DTOs like whether data is cached, when it was last refreshed, and collection totals + percentages
- RabbitMQ support for all endpoints

</td></tr><tr><td>### 1.1.0

15\. Jul 24

##### New

- Dawntrail Jobs parsing 
    - They were ignored before, now data is actually being parsed

</td></tr><tr><td>### 1.0.3

01\. Jul 24

##### Fixed

- Exception when parsing Dawntrail jobs

</td></tr><tr><td>### 1.0.2

15\. Apr 24

##### Fixed

- Exception when parsing free company rankings for free companies without ranking(s)

</td></tr><tr><td>### 1.0.1

27\. Jan 24

##### Fixed

- Exception when parsing disciples of the hand/land

</td></tr><tr><td>### 1.0.0

25\. Jan 24

##### Initial Release

- Character parsing 
    - Character
    - ClassJobs
    - Minions
    - Mounts
- Free Company parsing 
    - Free Company
    - Free Company members

</td></tr></tbody></table>

# RabbitMQ

<p class="callout danger">This feature was removed in version 4.0.0.</p>

### Queues (inbound)

<table border="1" id="bkmrk-name-rest-api-method" style="border-collapse: collapse; width: 100%; height: 400.774px;"><colgroup><col style="width: 25.0298%;"></col><col style="width: 32.895%;"></col><col style="width: 42.1944%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Name</td><td style="height: 29.7969px;">REST API method</td><td style="height: 29.7969px;">Associated Exchanges (see below for details)</td></tr></thead><tbody><tr style="height: 46.5938px;"><td style="height: 46.5938px;">netstone-get-character</td><td style="height: 46.5938px;">[/Character/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/Character/get_Character__lodestoneId_ "/Character/{lodestoneId}")</td><td style="height: 46.5938px;">netstone-get-character-result  
netstone-get-character-refreshed  
</td></tr><tr style="height: 57.7969px;"><td style="height: 57.7969px;">netstone-get-character-class-jobs</td><td style="height: 57.7969px;">[/Character/ClassJobs/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/Character/get_Character_ClassJobs__lodestoneId_ "/Character/ClassJobs/{lodestoneId}")</td><td style="height: 57.7969px;">netstone-get-character-class-jobs-result

netstone-get-character-class-jobs-refreshed

</td></tr><tr style="height: 57.7969px;"><td style="height: 57.7969px;">netstone-get-character-minions</td><td style="height: 57.7969px;">[/Character/Minions/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/Character/get_Character_Minions__lodestoneId_ "/Character/Minions/{lodestoneId}")</td><td style="height: 57.7969px;">netstone-get-character-minions-result

netstone-get-character-minions-refreshed

</td></tr><tr style="height: 57.7969px;"><td style="height: 57.7969px;">netstone-get-character-mounts</td><td style="height: 57.7969px;">[/Character/Mounts/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/Character/get_Character_Mounts__lodestoneId_ "/Character/Mounts/{lodestoneId}")</td><td style="height: 57.7969px;">netstone-get-character-mounts-result

netstone-get-character-mounts-refreshed

</td></tr><tr style="height: 57.7969px;"><td style="height: 57.7969px;">netstone-get-character-achievements</td><td style="height: 57.7969px;">[/Character/Achievements/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/Character/get_Character_Achievements__lodestoneId_ "/Character/Achievements/{lodestoneId}")</td><td style="height: 57.7969px;">netstone-get-character-achievements-result

netstone-get-character-achievements-refreshed

</td></tr><tr style="height: 35.3984px;"><td style="height: 35.3984px;">netstone-get-free-company</td><td style="height: 35.3984px;">[/FreeCompany/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/FreeCompany/get_FreeCompany__lodestoneId_ "/FreeCompany/{lodestoneId}")</td><td style="height: 35.3984px;">netstone-get-free-company-result

netstone-get-free-company-refreshed

</td></tr><tr style="height: 57.7969px;"><td style="height: 57.7969px;">netstone-get-free-company-members</td><td style="height: 57.7969px;">[/FreeCompany/Members/{lodestoneId}](https://api.netstone.tawmy.dev/swagger/index.html#/FreeCompany/get_FreeCompany_Members__lodestoneId_ "/FreeCompany/Members/{lodestoneId}")</td><td style="height: 57.7969px;">netstone-get-free-company-members-result

netstone-get-free-company-members-refreshed

</td></tr></tbody></table>

### Exchanges (outbound)

<table border="1" id="bkmrk-name-rest-api-method-1" style="border-collapse: collapse; width: 100%; height: 327.766px;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Name</td><td style="height: 29.7969px;">Sent When</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-result</td><td style="height: 29.7969px;">Character data requested through queue.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-refreshed</td><td style="height: 29.7969px;">Character data refreshed from Lodestone.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-class-jobs-result</td><td style="height: 29.7969px;">Character's class jobs requested through queue.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-class-jobs-refreshed</td><td style="height: 29.7969px;">Character's class jobs refreshed from Lodestone.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-minions-result

</td><td style="height: 29.7969px;">Character's minions requested through queue.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-minions-refreshed

</td><td style="height: 29.7969px;">Character's minions refreshed from Lodestone.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-mounts-result

</td><td style="height: 29.7969px;">Character's mounts requested through queue.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-mounts-refreshed

</td><td style="height: 29.7969px;">Character's mounts refreshed from Lodestone.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-achievements-result

</td><td style="height: 29.7969px;">Character's achievements requested through queue.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">netstone-get-character-achievements-refreshed

</td><td style="height: 29.7969px;">Character's achievements refreshed from Lodestone.</td></tr><tr><td>netstone-get-free-company-result

</td><td>Free Company data requested through queue.</td></tr><tr><td>netstone-get-free-company-refreshed

</td><td>Free Company data refreshed from Lodestone.</td></tr><tr><td>netstone-get-free-company-members-result

</td><td>Free Company's members requested through queue.</td></tr><tr><td>netstone-get-free-company-members-refreshed

</td><td>Free Company's members refreshed from Lodestone.  
</td></tr></tbody></table>

\- "result" exchanges are always (cached or not) filled when data was requested through inbound queue, NOT when requested through API.  
\- "refreshed" exchanges are filled whenever data was refreshed from the Lodestone, no matter whether through inbound queue or API. It is however NOT filled when data was returned from cache.

# Configuration and Deployment

Local and DEV deployments are done through a combination of two compose files.

- Local: compose.yml + compose.**local**.yml
- DEV: compose.yml + compose.**dev**.yml

Most environment variables are already set and committed to the repository. Some environment variables do not have values set, as they are confidential. Please set those values when deploying the compose project:

<table border="1" id="bkmrk-environment-variable" style="border-collapse: collapse; width: 100%; height: 148.984px;"><colgroup><col style="width: 27.3215%;"></col><col style="width: 72.7977%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Environment Variable</td><td style="height: 29.7969px;">  
</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">DB\_PASSWORD</td><td style="height: 29.7969px;">Password for Postgres database. Will be initialised with this value.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">RABBITMQ\_DEFAULT\_USER</td><td style="height: 29.7969px;">Default admin user for RabbitMQ. Please use a separate user for NetStone API.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">RABBITMQ\_DEFAULT\_PASS</td><td style="height: 29.7969px;">Default admin password for RabbitMQ. Please use a separate user for NetStone API.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">RABBITMQ\_USERNAME</td><td style="height: 29.7969px;">User for RabbitMQ authentication. Please do not use the default admin user.</td></tr><tr><td>RABBITMQ\_PASSWORD</td><td>User for RabbitMQ authentication. Please do not use the default admin user.</td></tr></tbody></table>

The RabbitMQ connection will fail before the user is created. The rest of the API will work just fine.