# fiken A Go client for [Fiken](https://fiken.no)'s API v2, and the OAuth2 flow that gets a token for it. ```sh go get simenandre.no/fiken ``` Two packages, and the split is the point: - **`fiken`** knows how to put a bearer token in a header and how to read Fiken's answers. Where the token came from is not its business. - **`fikenauth`** gets and keeps a token: the authorization-code flow over a loopback redirect, refreshing an expired grant, and a `0600` file to store it in. Refreshing is a side effect of asking for a token rather than a step the caller has to remember, which is the only way it gets done everywhere. ```go store := fikenauth.NewStore(filepath.Join(dir, "fiken-token.json")) source := fikenauth.NewSource(cfg, store) source.LoginCommand = "myapp fiken login" // named in the error if the grant dies client, err := fiken.NewWithTokens(source) ``` A personal API token works too, with no OAuth at all: ```go client, err := fiken.New(os.Getenv("FIKEN_TOKEN")) ``` ## What it is careful about **Amounts are integers.** `DraftLine.UnitPrice` is the invoice currency's minor unit and `Quantity` is normally 1, so the amount is carried entirely by the price and no float ever enters the payload. Fiken's minor unit has two decimals whatever the currency, so a price carried at more precision than that has to be rounded by the caller — where the rounding rule can be stated and tested, rather than here where it would be invisible. **A token never reaches a log line.** `Token.String` hides its own value, so `%v` on one prints nothing useful. **404 is its own error.** `ErrNoSuchResource` wraps it, because deleting a draft that is already gone is usually the state the caller wanted, not a failure. `ErrUnauthorized` is likewise separate from a transport error: one means log in again, the other means try again. **Retries are bounded and only for what is retryable.** A 5xx or 429 is retried; a 4xx is not, because sending the same bad request again cannot help. ## Status Extracted from a working invoicing tool and used by three of them, so the surface is what those needed rather than all of Fiken: companies, contacts, invoices, drafts, credit notes. Pull requests that widen it are welcome; the constraints above are not up for negotiation. Tests run against `httptest` servers — including retries, 4xx, and a declined consent — and reach nothing real.