Reporting
Our platform offers an industry-first approach to reporting that empowers your teams to get the data they need through a modern API interface while supporting conventional CSV standards for plugging into your existing processes and business intelligence tools.
The sections below detail both the methods for report definition (eg. how we set up and organize the queries that serve as the basis for our reports) and report delivery (eg. how partners can access reports in a secure manner).
Report Definition
We have pre-authored a large library of common reports used in benefit administration, and continually add more as requested by our customers. A few of the out-of-box reports include:
- Partner Summary Information
- Organization Summary Information
- Wallet Balances
- Card Issuing & Status
- Card Transactions
- Benefit Participant Summary Information
- Contributions
- Benefit Account Spending
- Claims
The current library of available reports is detailed below in the Report Library section. If a report you need is missing or you need a new way to view your data, contact your account manager for more information about creating custom reports.
Report Delivery
Partners have multiple ways to incorporate reporting into their administration processes by leveraging our unique platform capabilities. A few of those methods include:
-
By your own team via the GraphQL API. Specifically, see the generateReport mutation and report query and the instructions below.
-
Directly within the Health Wallet Manager app
Reporting API
First Dollar's reporting API makes it easy and straightforward for partners to access data about their own customers as needed, without needing to contact an account manager for an ad-hoc report. The data returned by these reports can then be pulled into the BI or other analytics system of choice.
Report generation is an asynchronous process:
- First, a partner will need to authenticate with the API using the OAuth Authentication Flow for the Partner API. The token returned by that call will then need to be passed as an Authentication: Bearer token on all subsequent requests, as outlined in the authentication flow documentation.
- Report requests are initiated using the the generateReport mutation. Importantly, this mutation will not return the full data of the requested report synchronously. Instead, on success it returns a ReportExecution object which gives the status of the report request and an ID value that can be used to retrieve the report results.
- Report status and results are retrieved with the report query. This query takes the report ID returned previously by a successfull call to
generateReportand, on success, returns a Report object that includes both anexecutionfield that will return the status of the report, and if the status isSUCCEEDED, theresultsfield will contain the actual ReportResults for the report. If report generation has not yet finished, or the report generation failed, theresultsfield will be null. Theresultsfield is intended for bounded pages only. Pages that are too large to return inline, and large exports in general, must be retrieved throughdownloadLinks; see Retrieving large reports.
Here is a sample set of calls:
Sample generateReport mutation:
mutation {
generateReport(
input: {
reportCode: "partner_info"
# If the report takes parameters, they would be passed in the reportParameters field
# reportParameters: { some_input_param: "value" }
}
) {
__typename
... on ReportExecution {
id
status
reportCode
reportTitle
reportParameters
requestedColumns
}
... on Error {
message
code
retryable
}
}
}
Sample generateReport response:
{
"data": {
"generateReport": {
"__typename": "ReportExecution",
"id": "21cd19cc-7c77-481d-8f21-3892b2a1c92e",
"status": "NOT_STARTED",
"reportCode": "partner_info",
"reportTitle": "Reports one row for a partner with just some metadata about the partner.",
"reportParameters": {
"partner_code_param": "demopartner_000"
},
"requestedColumns": null
}
}
}
Sample report query:
query {
# The id value here should be the id returned by the generateReport mutation
# Note that large reports will be paginated
report(where: { id: "21cd19cc-7c77-481d-8f21-3892b2a1c92e" }, page: 1) {
__typename
... on Report {
execution {
id
status
reportCode
reportTitle
reportParameters
requestedColumns
}
results {
columnHeaders
totalPages
rows
}
}
... on Error {
message
code
retryable
}
}
}
Sample report response:
{
"data": {
"report": {
"__typename": "Report",
"execution": {
"id": "21cd19cc-7c77-481d-8f21-3892b2a1c92e",
"status": "SUCCEEDED",
"reportCode": "partner_info",
"reportTitle": "Reports one row for a partner with just some metadata about the partner.",
"reportParameters": {
"partner_code_param": "demopartner_000"
},
"requestedColumns": null
},
# Since the execution status was SUCCEEDED, the results are shown below.
"results": {
"columnHeaders": [
"partnerCode",
"partnerName",
"partnerCreatedAt"
],
"totalPages": 1,
# the rows field contains the actual data for the report
"rows": [
{
"partnerCode": "demopartner_000",
"partnerName": "A Demo Partner",
"partnerCreatedAt": "2023-07-12 22:44:48.580723+00"
}
]
}
}
}
}
Retrieving large reports
Something to keep in mind while requesting reports is how the data is returned, and what limits are in place. The results field returns report data inline, as JSON, in the response itself. That makes it convenient for small reports, but it is intended for bounded responses only. A page is returned in results only if it is within the inline limits:
- Rows: no more than 10,000 data rows by default. The CSV header row is not counted.
- Size: within a maximum size in bytes.
Keep in mind that these limits can differ between environments, and they apply to the single page you request (the page argument), not to the report as a whole. A report with many pages is fine as long as the page you ask for is within the limits.
If the requested page is over a limit, the report query does not return its data. It returns a BadRequestError with the code REPORT_RESULTS_TOO_LARGE:
{
"data": {
"report": {
"__typename": "BadRequestError",
"message": "Report page=1 is too large to return inline (ROW_LIMIT_EXCEEDED). Request it through downloadLinks instead.",
"code": "REPORT_RESULTS_TOO_LARGE",
"retryable": false
}
}
}
Because retryable is false, sending the same request again will not succeed. Make sure that you handle the code rather than the message text, which can change.
Large reports, and any page you expect to be large, should be retrieved through downloadLinks. Each link downloads one page of the report as a CSV file. To retrieve a page that was rejected, run the report query again for the same page, without selecting results, and select downloadLinks instead:
query {
# Omit the page argument to get links for ALL pages of the report
report(where: { id: "21cd19cc-7c77-481d-8f21-3892b2a1c92e" }, page: 1) {
__typename
... on Report {
execution {
id
status
}
downloadLinks {
ordinal
totalFiles
downloadUrl
requestHeaders
expires
}
}
... on Error {
message
code
retryable
}
}
}
Request each file with an HTTP GET to its downloadUrl, passing the requestHeaders along with the request. Those headers can contain decryption keys, so treat them as sensitive. A link stops working at its expires time, so download the file promptly or run the query again for a fresh link.
To see how large each page is before you ask for it, select the files field on Report, or the rowCount and byteSize fields on downloadLinks. files lists every file of the report with its size, row count, and download status, whatever page you passed:
query {
report(where: { id: "21cd19cc-7c77-481d-8f21-3892b2a1c92e" }) {
__typename
... on Report {
files {
ordinal
totalFiles
rowCount
byteSize
status
}
}
... on Error {
message
code
retryable
}
}
}
A page with a rowCount or byteSize above the inline limits will be rejected if you request it through results, so use downloadLinks for it. Reports generated before per-file details were recorded return null for rowCount and byteSize; null means unknown, not zero. When in doubt, requesting downloadLinks ensures nothing is errored or skipped.
Report Library
The following list describes the current set of available reports. Again, if you need a report which is not listed here, please reach out to your account manager. Reports are listed by their reportCode, which is the value passed in to the generateReport mutation to specify the report to run. Unless otherwise specified, all reports take the following parameters:
- organization_code_param The code of an organization (i.e. employer) that belongs to the partner making the call, or an array of such codes (e.g. a parent organization and its divisions). If passed, only rows relevant to the specified organization(s) (e.g. participants who are members of those organizations) will be returned. Both a single code (e.g.
"acme_000") and an array of codes (e.g.["acme_000", "acme_west_000"]) are accepted. - uid_param The user ID of a participant that belongs to the partner making the call.
partner_info
Reports one row for a partner with just some metadata about the partner.
Parameters
- This report takes no parameters, as it just returns metadata about the calling partner.
organization_info
Reports one row for each organization within a partner with just some metadata about the org.
Parameters
- This report takes the organization_code_param, in which case only a row for that single organization is returned, as opposed to all the organizations that belong to a partner.
offering_participant_enrollments
Returns one row for every participant enrollment in an offering, either currently live or terminated/expired
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
program_cards
Returns one row for each card tied to a particular program
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- program_public_ulid_param - pass this param to retrieve only cards that belong to the program specified by this ULID.
offering_card_transactions
Returns one row for each SUCCESSFUL card transaction tied to a particular offering
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- offering_public_ulid_param - pass this param to retrieve only transactions for this offering.
- card_public_ulid_param - pass this param to retrieve only transactions that were from this card.
offering_spending_transactions
Returns one row for each spend (outgoing) transaction for a participant in an offering
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- offering_public_ulid_param - pass this param to retrieve only transactions for this offering.
- min_transaction_date_param - pass this param to retrieve only transactions that were made after this date.
- max_transaction_date_param - pass this param to retrieve only transactions that were made before this date.
offering_participant_funding_events
Returns one row for each funding event for a participant in an offering
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- offering_public_ulid_param - pass this param to retrieve only funding events for this offering.
- min_funding_event_date_param - pass this param to retrieve only funding events that were made after this date.
- max_funding_event_date_param - pass this param to retrieve only funding events that were made before this date.
offering_participant_summary_info
Returns one row for each offering participant, along with a bunch of summary data about their offering usage
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- offering_public_ulid_param - pass this param to retrieve only information about participants for this offering.
hsa_ending_daily_balance
Returns the ending daily balance per HSA account for each day within a time span - the views default to year-to-date.
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- min_date_param - pass this param to retrieve daily balances from this date onwards.
- max_date_param - pass this param to retrieve daily balances from before this date.
hsa_current_balance_info
Returns one row for each user HSA account with current balance info, including for associated investment accounts
Parameters
- This report takes both the organization_code_param and uid_params as outlined above.
- hsa_account_open_param - pass
truefor this param to only retrieve information about accounts that are currently open. - hsa_account_funded_param - pass
truefor this param to only retrieve information about accounts that have been funded.