Implementation Guide
Create an income report flow, exchange the authorization code, and fetch the verification report.
Getting Started
Three API calls to generate a disposable income report.
Create a Widget link for income analysis
Exchange the authorization code for an access token
Fetch the income report
1. Create the Widget link
POST https://api.finx-s.qwist.cloud/onetime/income-report
{
"redirect_uri": "https://my-app.example.com/callback",
"language": "de",
"reporting_period": 12,
"accounts": [
{ "id": "DE89370400440532013000" }
],
"account_types": ["Giro account"],
"allow_multi_selection": false
}Parameters:
| Parameter | Required | Description |
|---|---|---|
redirect_uri | ✅ | Where the user is returned after the flow. |
reporting_period | Months of transaction history to analyse. API minimum: 4. Recommended minimum for lending: 12. Default: 12. If the account is newer than this, the report covers the available history. Cannot be set at the same time as sync_period. | |
accounts | Pre-fill the user's IBAN to skip bank selection. | |
account_types | Restrict to specific account types (e.g. "Giro account"). Skips the account type selection screen. | |
allow_multi_selection | true lets the user connect multiple accounts (all are aggregated in the report). false restricts to one account. | |
language | Widget UI language. Options: de, en, es, fr, ar. Default: de. |
Single vs multi-accountFor income and expense analysis, we recommend
allow_multi_selection: falseand explicitly passing the user's primary Giro account IBAN. This gives you a cleaner income picture from a single account, rather than aggregating across savings or credit card accounts.
Response:
{
"location": "https://widget.qwist.cloud/?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"id": "f3a7d9c2-8e1b-4f5a-b2d6-9c4e7a1f3b8d"
}Store the id - you need it to fetch the report in step 3. It is also returned as flow_id in the callback.
2. Exchange the authorization code
After successful completion of the flow, users are redirected to your specified redirect_uri with the following query parameters:
state: Your provided identifier to maintain session state and identify the returning usersuccess: Boolean indicating successful flow completioncode: Authorization code valid for 1 hour, used to obtain an access token (only present on successful completion)abort: Boolean indicating if the user canceled the flowflow_id: Unique identifier for accessing flow-specific resources
https://example.com/callback?code=eyJhbGciO...&state=7fe78733&success=true&flow_id=3ca31c37-...&abort=falseExchanging the Code for Tokens
Request an access token by calling POST /auth/token with the following payload
// Request
{
"grant_type": "authorization_code",
"code": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...",
"redirect_uri": "https://my-app.example.com/callback"
}
// Response
{
"access_token": "AoFmNJLDTW8jQtGSJ1iZeeoLiwNZ2ihz3iiCHGpuvE439nppuY...",
"expires_in": 3600,
"scope": "accounts=ro balance=ro transactions=ro offline",
"token_type": "Bearer",
"refresh_token": "RTfI2WNyK78NozupDH9ai8GPRbjjdVsXPPt..."
}Using and Maintaining Tokens
- Include the
access_tokenin theAuthorizationheader for all API requests - For ongoing access, store the
refresh_tokensecurely - Each refresh token usage returns a new refresh token that must replace the previous one
- Access is revoked if:
- No user activity occurs within 90 days
- The refresh token isn't regularly renewed
Token lengthsToken lengths can vary. Avoid fixed-size database fields for storing tokens. If a size limit is required, allocate at least 2048 bytes per token field.
Token lifetimes
| Type | Lifetime |
|---|---|
| Authorization code | 1 hour |
| Access token | 1 hour |
| Refresh token | 90 days |
3. Fetch the income report
See Fetching your Income Report for the endpoint and response reference.
