FireflyIIINet 6.7.8.1
dotnet add package FireflyIIINet --version 6.7.8.1
NuGet\Install-Package FireflyIIINet -Version 6.7.8.1
<PackageReference Include="FireflyIIINet" Version="6.7.8.1" />
<PackageVersion Include="FireflyIIINet" Version="6.7.8.1" />
<PackageReference Include="FireflyIIINet" />
paket add FireflyIIINet --version 6.7.8.1
#r "nuget: FireflyIIINet, 6.7.8.1"
#:package FireflyIIINet@6.7.8.1
#addin nuget:?package=FireflyIIINet&version=6.7.8.1
#tool nuget:?package=FireflyIIINet&version=6.7.8.1
FireflyIIINet - the C# library for the Firefly III API
This is a .NET client for the Firefly III personal finance manager API.
This C# SDK is a hand-maintained client for the Firefly III API, built on Refit and System.Text.Json. It is kept in sync with the Firefly III OpenAPI specification manually — updates are applied directly to the source.
- API version: 6.7.8 (v1 endpoints, plus the two v2 endpoints last documented in 6.2.21)
- SDK version: 6.7.8.1 (tracks the API version; a 4th revision component denotes client-only changes)
- Source specification: api/firefly-iii-v6.7.8-v1.yaml (upstream stopped publishing v2 specs after 6.2.21)
- All released specifications (0.10.4 – 6.7.8): api/
Rule triggers and actions go beyond the specification. The spec's
RuleTriggerKeywordenum lists 36 values, but Firefly III accepts every search operator as a rule trigger (config/search.php, 144 of them) and returns them verbatim fromGET /rules; the same applies toRuleActionKeyword(config/firefly.php). Both enums therefore carry the server's full set, and an unrecognised value deserializes toUnknowninstead of failing the response. Don't writeUnknownback to the API — a rule read with an unknown keyword cannot be round-tripped.
Frameworks supported
- .NET 10.0
- .NET Framework 4.8
Dependencies
- Refit - 15.0.0 or later
- Refit.Reflection - version-locked to Refit (keeps
RestService.For<T>working on all targets) - System.Text.Json - 10.0.10 or later
- System.ComponentModel.Annotations - 5.0.0 or later
Installation
Build with dotnet build FireflyIIINet.sln, then reference the library and use the namespaces:
using FireflyIIINet.Api; // the Refit interfaces (IAccountsApi, ITransactionsApi, ...)
using FireflyIIINet.Client; // FireflyRefitSettings, SerializerOptions
using FireflyIIINet.Model;
using Refit;
This client is a set of Refit interfaces — one per API area
(IAboutApi, IAccountsApi, IAttachmentsApi, IAutocompleteApi, IAvailableBudgetsApi,
IBillsApi, IBudgetsApi, ICategoriesApi, IChartsApi, IConfigurationApi, ICurrenciesApi,
IDataApi, IInsightApi, ILinksApi, INetWorthApi, IObjectGroupsApi, IPiggyBanksApi,
IPreferencesApi, IRecurrencesApi, IRuleGroupsApi, IRulesApi, ISearchApi, ISummaryApi,
ITagsApi, ITransactionsApi, ITransactionsSumApi, IUsersApi, IWebhooksApi).
You build a client with an HttpClient plus the settings from FireflyRefitSettings.Create(),
which wire up the library's System.Text.Json options and the URL parameter formatting the
Firefly III API expects (lowercase booleans, [EnumMember] enum values, yyyy-MM-dd dates).
Auth is a personal access token supplied as a bearer token on the HttpClient.
Getting Started
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using FireflyIIINet.Api;
using FireflyIIINet.Client;
using FireflyIIINet.Model;
using Refit;
var http = new HttpClient { BaseAddress = new Uri("https://demo.firefly-iii.org/api") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<personal access token>");
var accounts = RestService.For<IAccountsApi>(http, FireflyRefitSettings.Create());
var list = await accounts.ListAccount(type: AccountTypeFilter.Asset);
foreach (var account in list.Data)
Console.WriteLine($"{account.Id}: {account.Attributes.Name}");
A runnable end-to-end example (create + delete a transaction) lives in src/Example/.
Servers behind Cloudflare need a
User-Agent.HttpClientsends none by default, and Cloudflare's bot protection answers such requests with 403 before they reach Firefly III. The public demo (https://demo.firefly-iii.org) is one of them. Add one to the client:http.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");.No native AOT. The SDK uses reflection-based System.Text.Json and Refit's reflection request builder, so it does not work in an app published with native AOT, or with
JsonSerializerIsReflectionEnabledByDefaultoff: every response fails to deserialize. A .NET 10 file-based app turns AOT on by default; add#:property PublishAot=false.
Errors
Refit throws Refit.ApiException for non-success status codes; the JSON error payload is
available via ex.Content and can be deserialized to the spec's error models
(ValidationError, NotFound, Unauthenticated, ...) with SerializerOptions.Default.
Notes
Transaction/TransactionSplitStorepiggy bank properties intentionally serialize explicitnullvalues (piggy_bank_id,piggy_bank_name) — the API distinguishes absent from null.- Chart data sets (
ChartDataSet,ChartDataSetV2) carryentriesas the server sends them, an object of label to value, which the spec mis-declares as an object withitems.ChartEntriesConverterreads it (and PHP's[]for an empty set) into a list of points in the server's order, each withKeyandValue. - Optional inputs are nullable: Refit parameters that default to
null, model constructor parameters that are not required, and[AllowNull]on optional model properties, so a nullable-enabled consumer can pass and assignnull("leave unset") without warnings. The library itself compiles with<Nullable>annotations</Nullable>; getters keep their declared types, so reading an optional field still needs the caller's own null check. - Fields the spec marks both required and
nullable: true(a piggy bank'starget_amount, a recurrence'srepeat_until, a rule action'svalue, ...) takenullin their constructors and always send the key,nullincluded. - A request sends only what the caller set. Optional fields left unset are not sent, rather
than as
nullor0001-01-01, which the server rejects (AccountStorewithout a currency failed with "The currency id must be a number."). On the*Updatemodels theboolandintfields arebool?/int?and are left out while null, so an update changes only what was set: a rename used to sendactive: false, deactivating a budget, bill, recurrence, rule group or webhook. On the*Storemodelsactivedefaults totrue, an unsetorder(0) is left for the server to assign, and a recurrence repetition'sweekenddefaults to1. - Some updates need more than the spec requires:
CategoryUpdateneedsName,RecurrenceUpdateneedsTransactions, andTransactionLinkUpdateneedsLinkTypeId,InwardIdandOutwardId. Without them the server answers 500 or 422. - Piggy banks follow the server where the spec differs:
PiggyBankAccountStore.Idis sent asaccount_id, andPiggyBankStore/PiggyBankUpdatehaveTransactionCurrencyIdandTransactionCurrencyCode(one of them is required on create), which the spec leaves out. - Responses are read as the server sends them: a
stringproperty also accepts a JSON number (an available budget'sspent_in_budgets[].currency_idis1), a date-only field also accepts a date-time (a recurrence'sfirst_date), and a budget without a currency of its own has a nullCurrencyDecimalPlaces. IDataApi.DestroyData'sunusedis abool?: the spec types it as an int64 array, but the server validates it astrue/false.GET /v1/release-notes/{version}has two methods:GetReleaseNotes(version), andGetCurrentReleaseNotes()for the server's own version (the route makes{version}optional). The model'srelease_notesfield isReleaseNotesHtml, since a member cannot share theReleaseNotestype's name.CategoryUpdate.NotesandCategoryUpdate.Colorare three-state, as the server treats them: never assigned (or null in the constructor) leaves the key out and the stored value unchanged; assigningnullsendsnulland clears it; assigning a value sets it.- The other updates (
AccountUpdate,BillUpdate,BudgetUpdate,BudgetLimitUpdate,PiggyBankUpdate,RecurrenceUpdate,TransactionSplitUpdate) leavenotesout whenNotesis null, which keeps the stored notes; assign""to clear them.TransactionLinkUpdateis the exception: the server removes a link's notes when the key is left out, so send the current notes again to keep them. (Attachments could not be checked against the demo.) - Date-only fields are written as
yyyy-MM-ddviaOpenAPIDateConverter; date-time fields use RFC 3339. - The test suite never hits the network — Refit slice tests use a capturing handler that records the outgoing request and returns canned JSON.
Development
There is no code generator in this repo. When the spec changes, edit the Refit interfaces under
src/FireflyIIINet/Api and the models under src/FireflyIIINet/Model directly, and keep
the spec copy under api/ in sync.
dotnet build FireflyIIINet.sln
dotnet test FireflyIIINet.sln
# Check whether upstream has published API specs not yet vendored in api/,
# and (--diff) how far the pinned SDK spec is behind the newest upstream v1.
# This is a .NET 10 file-based app (needs the .NET 10 SDK; the library targets net8.0).
# Upstream publishes one branch per release, so the check scans every branch (~22 GitHub API
# requests); set GITHUB_TOKEN or GH_TOKEN to stay clear of the 60/h anonymous limit.
dotnet run scripts/check-spec-version.cs -- --diff
# Read-only live smoke test against a real server (GETs only): the 6.7.x additions and every
# top-level list (features the server has switched off are skipped). Works against the public demo,
# https://demo.firefly-iii.org (demo@firefly-iii.org / demo): create a personal access token
# under Options > Profile > OAuth.
FIREFLY_TOKEN=<token> dotnet run scripts/smoke-test.cs -- https://demo.firefly-iii.org
A scheduled workflow (spec-check.yml) runs the same check weekly (without --diff); when
new upstream specs appear it opens a spec-check-labeled issue, comments on that issue on
later runs while they stay unvendored, and closes it once api/ is complete again.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- Refit (>= 15.0.0)
- Refit.Reflection (>= 15.0.0)
- System.ComponentModel.Annotations (>= 5.0.0)
- System.Text.Json (>= 10.0.10)
-
net10.0
- Refit (>= 15.0.0)
- Refit.Reflection (>= 15.0.0)
- System.ComponentModel.Annotations (>= 5.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.7.8.1 | 40 | 10/13/2026 |
| 6.7.8 | 48 | 10/12/2026 |
| 6.7.2.1 | 45 | 10/12/2026 |
| 6.7.2 | 109 | 9/16/2026 |
| 6.7.1 | 100 | 9/16/2026 |
| 6.7.0 | 106 | 9/16/2026 |
| 6.6.6.2 | 140 | 8/15/2026 |
| 6.6.6.1 | 120 | 8/15/2026 |
| 6.6.6 | 127 | 8/12/2026 |
| 6.6.2 | 125 | 8/12/2026 |
| 6.5.5 | 122 | 8/12/2026 |
| 6.5.1 | 126 | 8/12/2026 |
| 6.4.17 | 126 | 8/12/2026 |
| 6.4.16 | 119 | 8/12/2026 |
| 6.4.14 | 123 | 8/12/2026 |
| 6.4.8 | 115 | 8/12/2026 |
| 6.4.4 | 118 | 8/12/2026 |
| 6.4.3 | 115 | 8/12/2026 |
| 6.4.2 | 137 | 8/12/2026 |
| 6.4.0 | 121 | 8/12/2026 |
6.7.8.1, client-only fixes against Firefly III API 6.7.8, checked against the live demo. Requests send only what the caller set: unset optional fields are no longer sent as null or 0001-01-01, which the server rejected (StoreAccount/UpdateAccount without a currency, StoreTransactionLink without a link type name). Breaking: the bool/int fields of the *Update models are now bool?/int? and left out while null; a rename used to send active:false and deactivate the budget, bill, recurrence, rule group or webhook, and order:0 (rejected for rules). *Store models: active defaults to true (budgets, bills, rule groups and recurrences were created inactive), an unset order is left to the server, and a recurrence repetition's weekend defaults to 1. Piggy banks: PiggyBankAccountStore sends account_id (the server's name; the spec says id), and PiggyBankStore/PiggyBankUpdate gain TransactionCurrencyId/TransactionCurrencyCode, one of which the server requires; piggy banks could not be created before. Responses: string properties accept JSON numbers (ListAvailableBudgets failed), date-only fields accept date-times (every recurrence failed to read), and BudgetProperties.CurrencyDecimalPlaces is int? (a budget without its own currency failed to read). CategoryUpdate.Notes and Color are three-state: unassigned keeps the stored value, null clears it. Docs: Notes = "" clears notes on the other updates; TransactionLinkUpdate and RecurrenceUpdate need fields the spec calls optional; servers behind Cloudflare need a User-Agent; no native AOT. scripts/smoke-test.cs now also reads every top-level list.