Asynchronous report generation: request a report, poll its status, then download it once ready — the three-step flow Banking Circle uses for reports too large to return synchronously (reconciliation, account activity, rejections, bank statements, camt.053, etc).
The flow
POST /api/v2/reports/requests/{report-type} -> 202, Location header with request id
GET /api/v2/reports/{request-id}/get-status -> 202 Processing (Retry-After) | 302 Complete (Location: reportId)
GET /api/v1/reports/download-report/{reportId} -> the report bodyrequest_report/3 performs step 1 and returns just the request id
(read from the Location response header, per the documented flow).
poll_status/2 performs one status check. fetch_report/3 drives the
whole flow for you, sleeping between polls per the server's
Retry-After guidance (falling back to a fixed interval if absent) up
to a caller-supplied timeout — this is almost always what you want
unless you're building your own background job around the individual
steps.
Report type identifiers
Accepts either a known atom (translated to the slug Banking Circle
expects — see BankingCircle.Reporting.ReportType.known/0) or a raw
string, so you're not blocked on us keeping an exhaustive list current:
pass e.g. "held-payments-sanctions-screening" directly if we haven't
added a matching atom yet.
Summary
Functions
Downloads a generated report by its report id (obtained from
poll_status/2's {:complete, report_id}).
Drives the full request → poll → download flow, blocking (via
Process.sleep/1 between polls) until the report is ready or
timeout_ms elapses.
Checks the status of a previously-requested report.
Fetches the Standard Reconciliation Report synchronously
(GET /api/v1/reports/reconciliation-report) — a direct alternative to
the async request_report/3 → poll_status/2 → download/2 flow,
specifically for reconciliation. Handles up to 50,000 payments per
report inline; for larger date ranges or other report types, use the
async flow (fetch_report/3 or the individual steps) instead.
Kicks off report generation and returns {:ok, request_id} — extracted
from the response's Location header, per the documented flow (the
request id is not in the response body). Pass the id to poll_status/2,
or just use fetch_report/3 to handle the whole flow.
Types
Functions
@spec download(String.t(), client()) :: {:ok, term()} | {:error, BankingCircle.Error.t()}
Downloads a generated report by its report id (obtained from
poll_status/2's {:complete, report_id}).
@spec fetch_report(report_type(), map(), keyword()) :: {:ok, term()} | {:error, :timeout | BankingCircle.Error.t()}
Drives the full request → poll → download flow, blocking (via
Process.sleep/1 between polls) until the report is ready or
timeout_ms elapses.
For a web request handler, prefer request_report/3 plus a background
job that calls poll_status/2 on a schedule instead of blocking a
request process for a potentially multi-minute report generation —
fetch_report/3 is meant for scripts, IEx sessions, and background
workers that already expect to block.
@spec poll_status(String.t(), client()) :: {:processing, non_neg_integer() | nil} | {:complete, String.t()} | {:error, BankingCircle.Error.t()}
Checks the status of a previously-requested report.
Returns {:processing, retry_after_ms}, {:complete, report_id}, or
{:error, %BankingCircle.Error{}}.
@spec reconciliation_report( keyword(), client() ) :: {:ok, term()} | {:error, BankingCircle.Error.t()}
Fetches the Standard Reconciliation Report synchronously
(GET /api/v1/reports/reconciliation-report) — a direct alternative to
the async request_report/3 → poll_status/2 → download/2 flow,
specifically for reconciliation. Handles up to 50,000 payments per
report inline; for larger date ranges or other report types, use the
async flow (fetch_report/3 or the individual steps) instead.
@spec request_report(report_type(), map(), client()) :: {:ok, String.t()} | {:error, BankingCircle.Error.t()}
Kicks off report generation and returns {:ok, request_id} — extracted
from the response's Location header, per the documented flow (the
request id is not in the response body). Pass the id to poll_status/2,
or just use fetch_report/3 to handle the whole flow.