This page covers the request lifecycle over the API: create a request, send the owner their link, track status, pull updates and download reports. All paths are relative to https://app.finshare.io/v1. See Authentication for keys and errors.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /report_types | The report catalog |
POST | /requests | Create a request. Returns the owner's consent_url. |
GET | /requests | List requests. Filters: external_ref, status, limit (1–200, default 50). |
GET | /requests/:id | Status, items, approval summary, rounds and reports |
POST | /requests/:id/consent_link | Issue a new owner link. Earlier unused links stop working. |
POST | /requests/:id/cancel | Before approval: cancel. After: revoke access. |
POST | /requests/:id/rounds | Refresh or roll forward |
GET | /requests/:id/rounds/:number/download | Every delivered report in a round, as a zip of Excel files |
GET | /reports/:id | Report metadata and the full tie-out result |
GET | /reports/:id/file?format=xlsx | Download one report as xlsx, csv or json |
GET | /reports/:id/raw | The raw QuickBooks response(s) behind a report |
Create a request
curl https://app.finshare.io/v1/requests \
-H "Authorization: Bearer fl_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deal-4471-financials" \
-d '{
"items": [
{ "report_type": "profit_and_loss", "period": { "kind": "fiscal_years", "years": 3 }, "options": { "summarize_by": "year" } },
{ "report_type": "profit_and_loss", "period": { "kind": "trailing_months", "months": 12 }, "options": { "summarize_by": "month" } },
{ "report_type": "balance_sheet", "period": { "kind": "as_of" } },
{ "report_type": "general_ledger", "period": { "kind": "trailing_months", "months": 12 } },
{ "report_type": "ar_aging_summary", "period": { "kind": "as_of" } }
],
"period_end": "2026-09-30",
"access_days": 90,
"refresh_policy": "approve_each",
"requester": { "name": "Dana Reyes", "email": "dana@harborpartners.example" },
"owner": { "name": "Sam Ortiz", "email": "sam@ortizplumbing.example" },
"message": "We are the buyer'\''s diligence team. These are the reports on our request list.",
"external_ref": "deal-4471",
"send_owner_email": true
}'Body
| Field | Required | Default | Notes |
|---|---|---|---|
items | Yes | 1 to 25 report items. See below. | |
period_end | No | Last completed month end | YYYY-MM-DD. Relative periods are measured from it. |
access_days | No | 30 | How long FinShare may hold the connection after approval. 1 or more, up to your account's maximum (365 days unless we've set it otherwise). The owner may shorten it. |
refresh_policy | No | approve_each | approve_each or standing. The owner may change standing to approve_each. |
requester.name | Yes | Shown to the owner. | |
requester.email | No | Shown to the owner. FinShare emails this person about the request if they're a FinShare user with notifications on. | |
requester.org | No | Your account name | Shown to the owner as who is asking. |
owner.name, owner.email | No | Prefill the owner's details. owner.email is required if send_owner_email is true. | |
message | No | Up to 2,000 characters. Shown on the review page and in the email. | |
external_ref | No | Your own id, up to 200 characters. Returned on every webhook and filterable on GET /requests. | |
return_url | No | Where the owner goes after approving or declining. Must be on a registered origin. See Embedding. | |
send_owner_email | No | false | true to have FinShare email the owner the link. |
link_expires_in_days | No | 14 | 1 to 60. |
Unknown fields are rejected.
Report items
{
"report_type": "profit_and_loss",
"period": { "kind": "trailing_months", "months": 12 },
"basis": "accrual",
"options": { "summarize_by": "month" }
}report_type: one ofprofit_and_loss,balance_sheet,general_ledger,trial_balance,cash_flow,ar_aging_summary,ar_aging_detail,ap_aging_summary,ap_aging_detail,sales_by_customer.period: see Periods and basis for everykind. Aging reports needas_oforfixed_as_of; range reports reject them.basis:accrual(default) orcash. Ignored for reports without a basis.options.summarize_by:total,month,quarteroryear, on reports that support columns. Sending it for the general ledger or an aging report is an error.
Response
201 Created, with the request and the owner's link:
{
"id": "req_8fK2mQ7xLp3vRt9WnB4c",
"kind": "standard",
"connector_request_id": null,
"status": "awaiting_consent",
"external_ref": "deal-4471",
"created_at": "2026-10-28T15:02:11.000Z",
"period_end": "2026-09-30",
"access_days": 90,
"refresh_policy": "approve_each",
"link_expires_at": "2026-11-11T15:02:11.000Z",
"viewed_at": null,
"requester": { "name": "Dana Reyes", "email": "dana@harborpartners.example", "org": "Harbor Partners" },
"owner": { "name": "Sam Ortiz", "email": "sam@ortizplumbing.example" },
"message": "We are the buyer's diligence team. These are the reports on our request list.",
"return_url": null,
"items": [
{
"id": "itm_3HcN8tVb2QwYk5Rm7ZpD",
"report_type": "profit_and_loss",
"period": { "kind": "fiscal_years", "years": 3 },
"basis": "accrual",
"options": { "summarize_by": "year" },
"description": "Profit and Loss · The last 3 complete fiscal years on or before September 30, 2026 · accrual basis · by year"
}
],
"grant": null,
"rounds": [],
"consent_url": "https://app.finshare.io/c/..."
}items is shortened here. The consent_url is returned only on create. If you lose it, issue a new one.
Send or re-send the owner link
curl -X POST https://app.finshare.io/v1/requests/req_8fK2mQ7xLp3vRt9WnB4c/consent_link \
-H "Authorization: Bearer fl_live_..." \
-H "Content-Type: application/json" \
-d '{ "send_owner_email": true, "link_expires_in_days": 14 }'Returns 201 with { "consent_url": "...", "expires_at": "..." }. Every earlier unused link for the request stops working. It only works while the request is awaiting_consent. If your app shows a "Connect QuickBooks" button, call this on each click and redirect to the new link.
Track status
GET /requests/:id returns the same shape as create, without consent_url. Once the owner approves:
{
"status": "active",
"grant": {
"status": "active",
"company_name": "Ortiz Plumbing LLC",
"connection_status": "active",
"refresh_policy": "approve_each",
"access_until": "2027-01-26T16:40:03.000Z",
"approved_at": "2026-10-28T16:40:03.000Z",
"approver_name": "Sam Ortiz",
"approver_email": "sam@ortizplumbing.example",
"revoked_at": null,
"revoked_by": null
},
"rounds": [
{
"number": 1,
"type": "initial",
"mode": "full",
"period_end": "2026-09-30",
"status": "delivered",
"approved_via": "owner",
"approved_at": "2026-10-28T16:40:03.000Z",
"completed_at": "2026-10-28T16:41:12.000Z",
"items": [
{ "item_id": "itm_3HcN8tVb2QwYk5Rm7ZpD", "report_type": "profit_and_loss", "start": "2023-01-01", "end": "2025-12-31", "description": "Profit and Loss · January 2023 - December 2025 · accrual basis · by year" }
],
"reports": [
{
"id": "rpt_Vn4Qx8Lm2Tb6Wk9Rc3Hp",
"request_id": "req_8fK2mQ7xLp3vRt9WnB4c",
"report_type": "profit_and_loss",
"title": "Profit and Loss",
"period_start": "2023-01-01",
"period_end": "2025-12-31",
"basis": "accrual",
"company_name": "Ortiz Plumbing LLC",
"status": "ready",
"error": null,
"created_at": "2026-10-28T16:40:31.000Z",
"files": {
"xlsx": "https://app.finshare.io/v1/reports/rpt_Vn4Qx8Lm2Tb6Wk9Rc3Hp/file?format=xlsx",
"csv": "https://app.finshare.io/v1/reports/rpt_Vn4Qx8Lm2Tb6Wk9Rc3Hp/file?format=csv",
"json": "https://app.finshare.io/v1/reports/rpt_Vn4Qx8Lm2Tb6Wk9Rc3Hp/file?format=json"
},
"tieout": { "passed": true, "checks": 6 }
}
]
}
]
}Request status is one of awaiting_consent, active, declined, expired, revoked, cancelled. Round and report statuses are listed in Request statuses. Report status is ready, tieout_failed or failed.
Rather than polling, subscribe to webhooks.
Refresh and roll forward
# Same periods again
curl -X POST https://app.finshare.io/v1/requests/req_8fK2mQ7xLp3vRt9WnB4c/rounds \
-H "Authorization: Bearer fl_live_..." \
-H "Content-Type: application/json" \
-d '{ "type": "refresh" }'
# Move the period end to October 31 and pull only the new month
curl -X POST https://app.finshare.io/v1/requests/req_8fK2mQ7xLp3vRt9WnB4c/rounds \
-H "Authorization: Bearer fl_live_..." \
-H "Content-Type: application/json" \
-d '{ "type": "roll_forward", "period_end": "2026-10-31", "mode": "incremental" }'period_enddefaults to the last completed month end. It must be after the request's current period end and not in the future.modeisfull(default) orincremental. See Roll forward.
Returns 201 with { "round": {...}, "approval_url": "...", "request": {...} }.
- Under a standing approval the round starts at once and
approval_urlisnull. - Otherwise FinShare emails the owner and returns the
approval_urlthey'll use. The round waits inawaiting_approval.
It returns 409 conflict if the request isn't active, another round is open, or the QuickBooks connection needs reconnecting.
Cancel or stop access
curl -X POST https://app.finshare.io/v1/requests/req_8fK2mQ7xLp3vRt9WnB4c/cancel \
-H "Authorization: Bearer fl_live_..."Before approval the request becomes cancelled and its links stop working. After approval the approval is revoked, the request becomes revoked, and FinShare disconnects from the company unless another live approval needs it. Delivered reports stay downloadable.
Download reports
curl -o pnl.xlsx \
"https://app.finshare.io/v1/reports/rpt_Vn4Qx8Lm2Tb6Wk9Rc3Hp/file?format=xlsx" \
-H "Authorization: Bearer fl_live_..."format is xlsx (default), csv or json. The Excel file is the one stored at delivery. CSV and JSON are rendered from the stored QuickBooks snapshot and tied out again before they're returned. A report that isn't ready returns 409 report_not_ready. The URLs in files need your API key; they aren't public links.
For a whole round as one zip of Excel files: GET /requests/:id/rounds/:number/download.
Tie-out detail
GET /reports/:id returns the report with its full tie-out result:
{
"tieout": {
"passed": true,
"checks": [
{ "name": "no_truncation", "passed": true, "detail": "1 response(s) complete" },
{ "name": "intuit_arithmetic", "passed": true, "detail": "18 sections sum to their totals" },
{ "name": "pnl_gross_profit", "passed": true, "detail": "GrossProfit ties" },
{ "name": "pnl_net_operating_income", "passed": true, "detail": "NetOperatingIncome ties" },
{ "name": "pnl_net_income", "passed": true, "detail": "NetIncome ties" },
{ "name": "rendered_matches_raw:xlsx", "passed": true, "detail": "64 rows and 256 amounts match the raw response" }
]
}
}Raw QuickBooks data
GET /reports/:id/raw returns { "data": [...] } with one entry per QuickBooks response behind the report (more than one for a chunked general ledger). Each has snapshot_id, intuit_report, query (the parameters FinShare sent), chunk_start, chunk_end, fetched_at, sha256 and response, the untouched QuickBooks JSON. Downloads of raw data are recorded in the audit log.
Report catalog
GET /report_types returns:
{
"data": [
{ "type": "profit_and_loss", "label": "Profit and Loss", "period_shape": "range", "supports_basis": true, "supports_summarize_by": true },
{ "type": "balance_sheet", "label": "Balance Sheet", "period_shape": "range_or_as_of", "supports_basis": true, "supports_summarize_by": true },
{ "type": "ar_aging_summary", "label": "A/R Aging Summary", "period_shape": "as_of", "supports_basis": false, "supports_summarize_by": false }
]
}(Shortened; the full list has all ten types.)