{"id":39734182,"url":"https://github.com/amcintosh/freshbooks-java-sdk","last_synced_at":"2026-01-18T11:03:35.739Z","repository":{"id":40524393,"uuid":"325333796","full_name":"amcintosh/freshbooks-java-sdk","owner":"amcintosh","description":"FreshBooks API wrapper","archived":false,"fork":false,"pushed_at":"2023-08-10T14:06:52.000Z","size":2750,"stargazers_count":2,"open_issues_count":5,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-05-01T21:16:50.820Z","etag":null,"topics":["freshbooks","freshbooks-api","hacktoberfest","sdk","sdk-java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amcintosh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["amcintosh"]}},"created_at":"2020-12-29T16:23:44.000Z","updated_at":"2024-05-01T21:16:50.821Z","dependencies_parsed_at":"2022-07-27T08:32:30.197Z","dependency_job_id":null,"html_url":"https://github.com/amcintosh/freshbooks-java-sdk","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/amcintosh/freshbooks-java-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcintosh%2Ffreshbooks-java-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcintosh%2Ffreshbooks-java-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcintosh%2Ffreshbooks-java-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcintosh%2Ffreshbooks-java-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amcintosh","download_url":"https://codeload.github.com/amcintosh/freshbooks-java-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcintosh%2Ffreshbooks-java-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28535156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T10:13:46.436Z","status":"ssl_error","status_checked_at":"2026-01-18T10:13:11.045Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["freshbooks","freshbooks-api","hacktoberfest","sdk","sdk-java"],"created_at":"2026-01-18T11:03:35.672Z","updated_at":"2026-01-18T11:03:35.730Z","avatar_url":"https://github.com/amcintosh.png","language":"Java","readme":"# FreshBooks Java SDK\n\n![Maven Central](https://img.shields.io/maven-central/v/net.amcintosh/freshbooks-sdk)\n[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/amcintosh/freshbooks-java-sdk/Run%20Tests)](https://github.com/amcintosh/freshbooks-java-sdk/actions?query=workflow%3A%22Run+Tests%22)\n\nA FreshBooks Java SDK to allow you to more easily utilize the [FreshBooks API](https://www.freshbooks.com/api).\n\n## Installation\n\nUsing Maven:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.amcintosh\u003c/groupId\u003e\n  \u003cartifactId\u003efreshbooks-sdk\u003c/artifactId\u003e\n  \u003cversion\u003e0.7.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nor Gradle:\n\n```groovy\nimplementation 'net.amcintosh:freshbooks-sdk:0.7.0'\n```\n\n## Usage\n\nSee [https://amcintosh.github.io/freshbooks-java-sdk/](https://amcintosh.github.io/freshbooks-java-sdk/) for Javadocs.\n\n### Configuring the API freshBooksClient\n\nYou can create an instance of the API client in one of two ways:\n\n- By providing your application's OAuth2 `clientId` and `clientSecret` and following through the auth flow, which\n  when complete will return an access token.\n- Or if you already have a valid access token, you can instantiate the client with that token, however token refresh\n  flows will not function without the application id and secret.\n\n```java\nimport net.amcintosh.freshbooks.FreshBooksClient;\n\nFreshBooksClient freshBooksClient = new FreshBooksClient.FreshBooksClientBuilder(\n        \"your application id\", \"your secret\", \"https://some-redirect\")\n        .build();\n```\n\nand then proceed with the auth flow (see below).\n\nOr\n\n```java\nimport net.amcintosh.freshbooks.FreshBooksClient;\n\nFreshBooksClient freshBooksClient = new FreshBooksClient.FreshBooksClientBuilder(\n        \"your application id\")\n    .withAccessToken(\"a valid token\")\n    .build();\n```\n\n#### Authoization flow\n\n_This is a brief summary of the OAuth2 authorization flow and the methods in the FreshBooks API Client\naround them. See the [FreshBooks API - Authentication](https://www.freshbooks.com/api/authentication) documentation._\n\nFirst, instantiate your FreshBooksClient with `clientId`, `clientSecret`, and `redirectUri` as above.\n\nTo get an access token, the user must first authorize your application. This can be done by sending\nthe user to the FreshBooks authorization page. The authorization URL can be obtained by calling\n`freshBooksClient.getAuthRequestUri()`. Once the user has clicked accept there, they will be\nredirected to your `redirectUri` with an access grant code.\n\nWhen the user has been redirected to your `redirectUri` and you have obtained the access grant code from the URL\nparameters, you can exchange that code for a valid access token.\n\n```java\nAuthorizationToken token = freshBooksClient.getAccessToken(accessGrantCode);\n```\n\nThis call stores the Authorization token in the FreshBooksClient for use, and returns the results to you\nfor later usage. Eg.\n\n```java\nFreshBooksClient freshBooksClient = new FreshBooksClient.FreshBooksClientBuilder(\n        \"808c90fa4f722ddd984ab8d88afc13427d3f6aad0898ec929cb2cb982835d5c7\")\n    .withAuthorizationToken(token);\n```\n\nThe `AuthorizationToken` contains all of the relevant details from the token response including the `accessToken`,\n`refreshToken`, `createdAt`, `expiresIn`, and the `scopes` the token is authorized for.\n\n```java\nassertEquals(\"my_access_token\", token.getAccessToken());\nassertEquals(\"my_refresh_token\", token.getRefreshToken());\nassertEquals(ZonedDateTime.of(2021, 7, 26, 16, 57, 6, 0, ZoneId.of(\"UTC\")), token.getCreatedAt());\nassertEquals(100, token.getExpiresIn());\nassertEquals(ZonedDateTime.of(2021, 7, 26, 16, 58, 46, 0, ZoneId.of(\"UTC\")), token.getExpiresAt());\nassertEquals(ImmutableList.of(\"some:scope\", \"some:other:scope\"), token.getScopes());\n```\n\nWhen the token expires, it can be refreshed with the `refreshToken` value in the `FreshBooksClient`:\n\n```java\nAuthorizationToken refreshedToken = freshBooksClient.refreshAccessToken();\nassertEquals(\"my_new_access_token\", refreshedToken.getAccessToken());\n```\n\n### Current User\n\nFreshBooks users are uniquely identified by their email across our entire product. One user may act on several\nBusinesses in different ways, and our Identity model is how we keep track of it. Each unique user has an Identity,\nand each Identity has Business Memberships which define the permissions they have.\n\nSee [FreshBooks API - Business, Roles, and Identity](https://www.freshbooks.com/api/me_endpoint) and\n[FreshBooks API - The Identity Model](https://www.freshbooks.com/api/identity_model).\n\nThe current user can be accessed by:\n\n```java\nIdentity currentUser = freshBooksClient.currentUser();\n\nString userEmail = currentUser.getEmail();\n\nList\u003cBusinessMembership\u003e businesses = currentUser.getBusinessMemberships();\nBusinessRole businessRole = businesses.get(0).getRole();\n```\n\n### Making API Calls\n\nEach resource in the client has provides calls for `get`, `list`, `create`, `update` and `delete` calls. Please note that some API resources are scoped to a FreshBooks `account_id` while others are scoped to a `business_id`. In general these fall along the lines of accounting resources vs projects/time tracking resources, but that is not precise.\n\nNot all resources have full CRUD functions available. For example expense categories have `list` and `get` calls, but\nare not deletable.\n\n```java\nimport net.amcintosh.freshbooks.models.Client;\nimport net.amcintosh.freshbooks.models.Project;\n\nClient client = freshBooksClient.clients().get(accountId, clientUserId);\nProject project = freshBooksClient.projects().get(businessId, projectId);\n```\n\nAlternatively, resources can be instantiated directly.\n\n```java\nimport net.amcintosh.freshbooks.resources.Clients;\nimport net.amcintosh.freshbooks.resources.Projects;\n\nClients clients = new Clients(freshBooksClient);\nClient client = clients.get(accountId, clientUserId);\n\nProject project = new Projects(freshBooksClient).get(businessId, projectId);\n```\n\n#### Get and List\n\nAPI calls with a single resource return a model Class of the appropriate resource.\nFor active vs deleted resources, see [FreshBooks API - Active and Deleted Objects](https://www.freshbooks.com/api/active_deleted).\n\n```java\nimport net.amcintosh.freshbooks.models.Client;\n\nClient client = freshBooksClient.clients().get(accountId, clientUserId);\n\nassertEquals(clientUserId, client.getId());\nassertEquals(\"FreshBooks\", client.getOrganization());\nassertEquals(VisState.ACTIVE, client.getVisState());\n```\n\nAPI calls with returning a list of resources return a Class extending `ListResult` containing a `List` of the resource model\nand pagination data (see Pagination below).\n\n```java\nimport net.amcintosh.freshbooks.models.Client;\nimport net.amcintosh.freshbooks.models.ClientList;\n\nClientList clientListResponse = freshBooksClient.clients().list(accountId);\nList\u003cClient\u003e clients = clientListResponse.getClients();\n\nassertEquals(\"FreshBooks\", clients.get(0).getOrganization());\n\nfor (Client client: clientListResponse.getClients()) {\n    assertEquals(\"FreshBooks\", client.getOrganization());\n}\n```\n\nResponse objects implements Map so the `get()` method can be used to access the JSON content. This can be useful if\nthe raw response data of a field is needed, or in the case where a field does not have a corresponding field in the\nreponse object. This is most common in the case of 'includes' which can have special cases or undocumented\nadditional fields.\n\n```java\nassertEquals(\"FreshBooks\", client.getOrganization());\nassertEquals(\"FreshBooks\", client.get(\"organization\"));\n\nassertEquals(VisState.ACTIVE, client.getVisState());\nassertEquals(0, client.get(\"vis_state\"));\n\nassertEquals(\"Unexpected Data\", client.get(\"some_undocumented_field\"));\n```\n\n#### Create, Update, and Delete\n\nAPI calls to create and update can either be called with a `Map` of the resource data,\nor a model Class with the data populated. A successful call will return a model Class\nas if a `get` call.\n\n_Note_: When using the `Map` of data, you need to specify the field as it exists in the \nFreshBooks API. There are API fields that are translated to more intuitive names in the \ndata models. For example `fname` = `firstName`, or `bus_phone` = `businessPhone`.\n\nCreate:\n\n```java\n// Create from object\nClient clientToCreateOne = new Client();\nclientToCreateOne.setEmail(\"john.doe@abcorp.com\");\nclientToCreateOne.setFirstName(\"John\");\n\nClient createdClientOne = freshBooksClient.clients().create(accountId, clientToCreateOne);\nlong createdClientOneId = createdClientOne.getId();\n\n// Update from data map\nHashMap\u003cString, Object\u003e clientToCreateTwo = new HashMap();\nclientToCreateTwo.put(\"email\", \"john.doe@abcorp.com\");\nclientToCreateTwo.put(\"fname\", \"John\");\n\nClient createdClientTwo = freshBooksClient.clients().create(accountId, clientToCreateTwo);\nlong createdClientTwoId = createdClientTwo.getId();\n```\n\nUpdate:\n\n```java\nClient existingClient = freshBooksClient.clients().get(accountId, clientUserId);\nassertEquals(\"john.doe@abcorp.com\", existingClient.getEmail());\n\n// Update from object\nexistingClient.setEmail(\"new.email@abcorp.ca\");\nexistingClient = freshBooksClient.clients().update(accountId, clientUserId, existingClient);\nassertEquals(\"new.email@abcorp.ca\", existingClient.getEmail());\n\n// Update from data map\nHashMap\u003cString, Object\u003e updateData = new HashMap();\nupdateData.put(\"email\", \"newer.email@abcorp.ca\");\nexistingClient = freshBooksClient.clients().update(accountId, clientUserId, updateData);\nassertEquals(\"new.email@abcorp.ca\", existingClient.getEmail());\n```\n\nDelete:\n\n```java\nClient client = clients.delete(accountId, clientUserId);\n\nassertEquals(VisState.DELETED, client.getVisState());\n```\n\n#### Error Handling\n\nCalls made to the FreshBooks API with a non-2xx response result in a `FreshBooksException`.\nThis contains the error message, HTTP response code, FreshBooks-specific error number if one exists,\nand in the case of invalid values, the offending field, value, and a validation message.\n\nExample:\n\n```java\ntry {\n    clientResponse = freshBooksClient.clients().get(accountId, 12345); //652471);\n} catch (FreshBooksException e) {\n    assertEquals(\"Client not found.\", e.getMessage());\n    assertEquals(\"NOT FOUND\", e.statusMessage);\n    assertEquals(404, e.statusCode);\n    assertEquals(1012, e.errorNo);\n    assertEquals(\"ValidationError in client. userid='12345'.\", e.getValidationError());\n```\n\n#### Pagination, Filters, Includes, and Sorting\n\n`list` calls can take a List of QueryBuilder objects that can be used to paginate, filter, and include\noptional data in the response. See [FreshBooks API - Parameters](https://www.freshbooks.com/api/parameters) documentation.\n\n##### Pagination\n\nPagination results are included in `ListResult` responses:\n\n```java\nimport net.amcintosh.freshbooks.models.Pages;\n\nClientList clientListResponse = freshBooksClient.clients().list(accountId);\nPages pageResult = clientListResponse.getPages();\n\nassertEquals(\"Pages{page=1, pages=1, perPage=30, total=6}\", pageResult.toString());\nassertEquals(1, pageResult.getPage());\nassertEquals(1, pageResult.getPages());\nassertEquals(30, pageResult.getPerPage());\nassertEquals(6, pageResult.getTotal());\n```\n\nTo make change a paginated call, pass a `PaginationQueryBuilder` object into the `list` call.\n\n```java\nimport net.amcintosh.freshbooks.models.builders.PaginationQueryBuilder;\n\nPaginationQueryBuilder paginator = new PaginationQueryBuilder(2, 4);\nArrayList\u003cQueryBuilder\u003e builders = new ArrayList();\nbuilders.add(paginator);\n\nClientList clientListResponse = freshBooksClient.clients().list(accountId, builders);\n\nassertEquals(\"Pages{page=2, pages=3, perPage=4, total=9}\", clientListResponse.getPages().toString());\n```\n\n`PaginationQueryBuilder` can also be updated with the `page` and `perPage` functions.\n\n```java\nPaginationQueryBuilder paginator = new PaginationQueryBuilder(2, 4);\nassertEquals(\"PaginationQueryBuilder{page=2, perPage=4}\", paginator.toString());\n\npaginator.page(3).perPage(5);\nassertEquals(\"PaginationQueryBuilder{page=3, perPage=5}\", paginator.toString());\n```\n\n##### Filters\n\nTo filter which results are return by `list` method calls, construct a `FilterQueryBuilder` and pass that\nin the list of builders to the `list` method.\n\n```java\nimport net.amcintosh.freshbooks.models.builders.FilterQueryBuilder;\n\nFilterQueryBuilder filters = new FilterQueryBuilder();\nfilters.addEquals(\"userid\", 123);\nArrayList\u003cQueryBuilder\u003e builders = new ArrayList();\nbuilders.add(filters);\n\nClientList clientListResponse = freshBooksClient.clients().list(accountId, builders);\n\nassertEquals(123, clientListResponse.getClients.get(0).getId());\n```\n\nFilters can be built with the various methods for FreshBooks' `equals`, `in`, `like`, `between`, `boolean`,\nand `datetime` filters. The methods can be chained together.\n\nPlease see [FreshBooks API - Active and Deleted Objects](https://www.freshbooks.com/api/active_deleted)\nfor details on filtering active, archived, and deleted resources.\n\n```java\nFilterQueryBuilder filters = new FilterQueryBuilder();\n\nfilters.addLike(\"email_like\", \"@freshbooks.com\");\n// Yields '\u0026search[email_like]=@freshbooks.com'\n\nfilters.addInList(\"clientids\", Arrays.asList(123, 456));\n// Yields '\u0026search[clientids][]=123\u0026search[clientids][]=456'\n\nfilters.addBoolean(\"allow_late_fees\", true);\n// Yields '\u0026search[allow_late_fees]=true'\n\nfilters.addBetween(\"amount\", 1, 10);\n// Yields '\u0026search[amount_min]=1\u0026search[amount_max]=10'\n\nfilters.addBetween(\"amount_min\", 1).addBetween(\"amount_max\", 10);\n// Yields '\u0026search[amount_min]=1\u0026search[amount_max]=10'\n\nfilters.addBetween(\"start_date\", \"2020-11-21\");\n// Yields '\u0026search[start_date]=2020-11-21'\n\nfilters.addDateTime(\"updated_since\", ZonedDateTime.now());\n// Yields '\u0026updated_since=2022-01-19T13:14:07'\n\nfilters.addDateTime(\"updated_since\", LocalDate.now());\n// Yields '\u0026updated_since=2022-01-19'\n```\n\n##### Includes\n\nTo include additional relationships, sub-resources, or data in a list or get response, a `IncludesQueryBuilder`\ncan be constructed.\n\n```java\nimport net.amcintosh.freshbooks.models.builders.IncludesQueryBuilder;\n\nIncludesQueryBuilder includes = new IncludesQueryBuilder().include(\"outstanding_balance\");\nassertEquals(\"IncludesQueryBuilder{includes=[outstanding_balance]}\", includes.toString());\n```\n\nWhich can then be passed into `get` or `list` calls:\n\n```java\n// Get call\nClient client = freshBooksClient.clients().get(accountId, clientId, includes);\nassertEquals(\"USD\", client.getOutstandingBalance().getCode());\n\n// List call\nArrayList\u003cQueryBuilder\u003e builders = new ArrayList();\nbuilders.add(includes);\n\nClientList clientListResponse = freshBooksClient.clients().list(accountId, builders);\nList\u003cClient\u003e clients = clientListResponse.getClients();\nassertEquals(\"USD\", clients.get(0).getOutstandingBalance().getCode());\n```\n\n##### Sorting\n\nTo sort the results of a list call by supported fields (see the documentation for that resource) a\n`SortQueryBuilder` can be used.\n\n```java\nimport net.amcintosh.freshbooks.models.builders.SortQueryBuilder;\n\nSortQueryBuilder sort = new SortQueryBuilder().ascending(\"invoice_date\");\n// Yields '\u0026sort=invoice_date_asc'\n\nassertEquals(\"SortQueryBuilder{sort=invoice_date;ascending}\", sort.toString());\n```\n\nto sort by the invoice date in ascending order, or:\n\n```java\nSortQueryBuilder sort = new SortQueryBuilder().descending(\"invoice_date\");\n// Yields '\u0026sort=invoice_date_desc'\n\nassertEquals(\"SortQueryBuilder{sort=invoice_date;descending}\", sort.toString());\n```\n\nfor descending order.\n\nWhich can then be passed into `list` calls:\n\n```java\nArrayList\u003cQueryBuilder\u003e builders = new ArrayList();\nbuilders.add(sort);\n\nInvoiceList invoiceListResponse = freshBooksClient.invoices().list(accountId, builders);\n```\n","funding_links":["https://github.com/sponsors/amcintosh"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famcintosh%2Ffreshbooks-java-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famcintosh%2Ffreshbooks-java-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famcintosh%2Ffreshbooks-java-sdk/lists"}